home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Mac Game Programming Gurus / TricksOfTheMacGameProgrammingGurus.iso / More Source / C⁄C++ / Xconq 7.0d37 / source / x11 / xmap.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-05-04  |  75.9 KB  |  2,931 lines  |  [TEXT/KAHL]

  1. /* Map handling for the X11 interface to Xconq.
  2.    Copyright (C) 1987, 1988, 1989, 1991, 1992, 1993, 1994, 1995
  3.    Stanley T. Shebs.
  4.  
  5. Xconq is free software; you can redistribute it and/or modify
  6. it under the terms of the GNU General Public License as published by
  7. the Free Software Foundation; either version 2, or (at your option)
  8. any later version.  See the file COPYING.  */
  9.  
  10. /* This file contains the high-level and general handling for map
  11.    windows. */
  12.  
  13. #include "conq.h"
  14. #include "xconq.h"
  15.  
  16. void enable_in_unit_type_list PROTO ((Side *side, Map *map, int u, int flag));
  17.  
  18. extern void place_feature_legends PROTO ((Legend *leg, int nf, Side *side,
  19.                       int orient, int block));
  20. extern int seen_terrain_at PROTO ((int x, int y, Side *side));
  21. extern int smallest_image PROTO ((ImageFamily *imf, int *wp, int *hp));
  22. extern void zoom_in_out PROTO ((Side *side, Map *map, int which));
  23.  
  24. extern int find_side_and_map_via_ctrlpanel_form PROTO ((Widget w,
  25.                             Side **sidep,
  26.                             Map **mapp));
  27.  
  28. extern int min_w_for_unit_image;
  29. extern int min_h_for_unit_image;
  30.  
  31. extern time_t game_start_in_real_time;
  32. extern time_t turn_play_start_in_real_time;
  33.  
  34. extern void handle_map_sides_events PROTO ((Widget w, XtPointer clientdata,
  35.                         XEvent *evt, Boolean *contdispatch));
  36. extern void handle_map_info_events PROTO ((Widget w, XtPointer clientdata,
  37.                        XEvent *evt, Boolean *contdispatch));
  38.  
  39. void place_legends PROTO ((Side *side));
  40.  
  41. void scroll_map_absolute PROTO ((Side *side, Map *map, int sx, int sy));
  42. void scroll_map_relative PROTO ((Side *side, Map *map, int sx, int sy));
  43.  
  44. static void create_map_controls PROTO ((Side *side, Map *map));
  45.  
  46. static void interp_key_command PROTO ((Side *side, Map *map));
  47.  
  48. static void handle_map_click PROTO ((Side *side, Map *map, int sx, int sy));
  49.  
  50. static void mode_callback PROTO ((Widget w, XtPointer client_data,
  51.                   XtPointer call_data));
  52. static void command_callback PROTO ((Widget w, XtPointer client_data,
  53.                      XtPointer call_data));
  54. static void view_flag_callback PROTO ((Widget w, XtPointer client_data,
  55.                        XtPointer call_data));
  56. static void zoom_callback PROTO ((Widget w, XtPointer client_data,
  57.                   XtPointer call_data));
  58. static void unit_type_list_callback PROTO ((Widget w, XtPointer client_data,
  59.                         XtPointer call_data));
  60. static void panner_callback PROTO ((Widget w, XtPointer client_data,
  61.                     XtPointer call_data));
  62. static void porthole_callback PROTO ((Widget w, XtPointer client_data,
  63.                       XtPointer call_data));
  64.  
  65. static void panner_resize_handler PROTO ((Widget w, XtPointer client_data,
  66.                       XEvent *event, Boolean *cont));
  67.  
  68. static void MA_keypress PROTO ((Widget w, XEvent *event, String *params,
  69.                 Cardinal *num_params));
  70. static void MA_mapexpose PROTO ((Widget w, XEvent *event, String *params,
  71.                  Cardinal *num_params));
  72. static void MA_setcoord PROTO ((Widget w, XEvent *event, String *params,
  73.                 Cardinal *num_params));
  74. static void MA_movelook PROTO ((Widget w, XEvent *event, String *params,
  75.                 Cardinal *num_params));
  76. static void MA_moveunit PROTO ((Widget w, XEvent *event, String *params,
  77.                 Cardinal *num_params));
  78. static void MA_distance PROTO ((Widget w, XEvent *event, String *params,
  79.                 Cardinal *num_params));
  80. static void MA_motionsetcoord PROTO ((Widget w, XEvent *event, String *params,
  81.                       Cardinal *num_params));
  82. static void MA_toolaction PROTO ((Widget w, XEvent *event, String *params,
  83.                   Cardinal *num_params));
  84. static void MA_toolselection PROTO ((Widget w, XEvent *event, String *params,
  85.                      Cardinal *num_params));
  86. static void MA_movelook_ul PROTO ((Widget wdgt, XEvent *event, String *params,
  87.                    Cardinal *num_params));
  88. static void MA_movelook_l  PROTO ((Widget wdgt, XEvent *event, String *params,
  89.                    Cardinal *num_params));
  90. static void MA_movelook_dl PROTO ((Widget wdgt, XEvent *event, String *params,
  91.                    Cardinal *num_params));
  92. static void MA_movelook_ur PROTO ((Widget wdgt, XEvent *event, String *params,
  93.                    Cardinal *num_params));
  94. static void MA_movelook_r  PROTO ((Widget wdgt, XEvent *event, String *params,
  95.                    Cardinal *num_params));
  96. static void MA_movelook_dr PROTO ((Widget wdgt, XEvent *event, String *params,
  97.                    Cardinal *num_params));
  98. static void MA_center PROTO ((Widget wdgt, XEvent *event, String *params,
  99.                   Cardinal *num_params));
  100. static void MA_message PROTO ((Widget wdgt, XEvent *event, String *params,
  101.                    Cardinal *num_params));
  102. static void SetMessageArea PROTO ((Widget msgarea, char *msg));
  103.  
  104. static void movelook_one_cell PROTO ((Widget w, int xdelta, int ydelta));
  105.  
  106. static void popup_ctrlpanel PROTO ((Side *side, Map *map));
  107. static void popdown_ctrlpanel PROTO ((Side *side, Map *map));
  108. static void create_ctrlpanel PROTO ((Side *side, Map *map));
  109. static void ctrlpanel_cancel_callback PROTO ((Widget w, XtPointer client_data,
  110.                           XtPointer call_data));
  111. static void ctrlpanel_revert_callback PROTO ((Widget w, XtPointer client_data,
  112.                           XtPointer call_data));
  113. static void ctrlpanel_apply_callback PROTO ((Widget w, XtPointer client_data,
  114.                          XtPointer call_data));
  115. static void ctrlpanel_done_callback PROTO ((Widget w, XtPointer client_data,
  116.                         XtPointer call_data));
  117.  
  118. XtActionsRec map_actions_table[] = {
  119.     { "keypress", MA_keypress },
  120.     { "redraw", MA_mapexpose },
  121.     { "select", MA_setcoord },
  122.     { "movelook", MA_movelook },
  123.     { "moveunit", MA_moveunit },
  124.     { "distance", MA_distance },
  125.     { "motionselect", MA_motionsetcoord },
  126.     { "toolaction",  MA_toolaction },
  127.     { "toolselection", MA_toolselection },
  128.     { "movelook_ul", MA_movelook_ul },
  129.     { "movelook_l", MA_movelook_l },
  130.     { "movelook_dl", MA_movelook_dl },
  131.     { "movelook_ur", MA_movelook_ur },
  132.     { "movelook_r", MA_movelook_r },
  133.     { "movelook_dr", MA_movelook_dr },
  134.     { "center",    MA_center },
  135. };
  136.  
  137. void
  138. add_map_actions()
  139. {
  140.     extern XtAppContext thisapp;
  141.  
  142.     XtAppAddActions(thisapp, map_actions_table, XtNumber(map_actions_table));
  143. }
  144.  
  145. /* Given the total width and height of a map window, calculate the sizes
  146.    of the different subareas. */
  147.  
  148. static void
  149. subdivide_map(side, map)
  150. Side *side;
  151. Map *map;
  152. {
  153.     int fh = side->ui->fh, panhpref;
  154.  
  155.     /* Subdivide the window into right and left halves. */
  156.     if (map->leftfrac == 0)
  157.       map->leftfrac = 80;
  158.     map->leftw = (map->leftfrac * map->totalw) / 100 ;
  159.     /* Subdivide the left half. */
  160.     map->toph = max(100, (20 * map->totalh / 100));
  161.     map->pxw = map->leftw - 40 - 2 - 2;  /* this is dubious... */
  162.     /* (should be able to tweak this) */
  163.     map->infoh = 75;
  164.     map->pxh = map->totalh - map->toph - map->infoh - 4;
  165.     /* Subdivide the list area into upper and lower lists, plus panner
  166.        space. */
  167.     map->list1w = map->totalw - map->leftw;
  168.     /* All of these have the same width. */
  169.     map->list2w = map->panw = map->list1w;
  170.     /* Compute the height of the panner by scaling by aspect ratio of
  171.        the area. */
  172.     map->panh = (area.height * map->panw) / area.width;
  173.     if (map->panh > (map->totalh * 30) / 100) {
  174.     /* Panner subarea is too big; shrink the width to fit the
  175.        available height. */
  176.     map->panh = (map->totalh * 30) / 100;
  177.     map->panw = (area.width * map->panh) / area.height;
  178.     }
  179.     /* Inset the panner a bit. */
  180.     map->panw -= 8;  map->panh -= 8;
  181.     /* Compute the side list height to be enough to show all of them. */
  182.     map->list1h = numsides * map->sidespacing;
  183.     /* ...but don't let it use more than 60% of available height. */
  184.     map->list1h = min(map->list1h, (map->totalh * 60) / 100);
  185.     /* Middle list only gets the leftovers. */
  186.     map->list2h = map->totalh - map->list1h - map->panh;
  187. }
  188.  
  189. /* Create a generic map object. */
  190.  
  191. Map *
  192. create_map(side, power, geospec)
  193. Side *side;
  194. int power;
  195. char *geospec;
  196. {
  197.     int x, y, w, h, m, i, sx, sy, scale;
  198.     int fh = side->ui->fh;
  199.     long flags;
  200.     Map *map;
  201.     Dimension height;
  202.     XFontStruct    *xfsp;
  203.     Pixmap pic;
  204.     char wbuff[BUFSIZE];
  205.     Widget shell;
  206.     Display *dpy = side->ui->dpy;
  207.  
  208.     DGprintf("Creating map, mag power %d\n", power);
  209.     map = (Map *) xmalloc(sizeof(Map));
  210.  
  211.     /* 1000x750 pixels is "ideal" - big, but not really big. */
  212.     map->totalw = 1000;  map->totalh = 750;
  213.     if (!empty_string(geospec)) {
  214.     /* This map has been given a preferred size - work to it. */
  215.     flags = XParseGeometry(geospec, &sx, &sy, &w, &h);
  216.     if (flags & WidthValue)
  217.       map->totalw = w;
  218.     if (flags & HeightValue)
  219.       map->totalh = h;
  220.     /* Note that we allow the caller to specify geometries
  221.        that may be larger than the available screen, on the
  222.        theory that people specifying geometries explicitly
  223.        know what they're doing. */
  224.     } else {
  225.     /* Shrink to fit display if the default is too big. */
  226.     w = DisplayWidth(dpy, side->ui->screen);
  227.     h = DisplayHeight(dpy, side->ui->screen);
  228.     /* Leave a little room around the edges, but not too much;
  229.        Xconq needs all the screen space it can get. */
  230.     map->totalw = min(map->totalw, w - w / 16);
  231.     map->totalh = min(map->totalh, h - h / 16);
  232.     }
  233.     /* Compute the vertical spacing for the side list entries. */
  234.     map->sidespacing = fh + 12;
  235.     /* Add room to display per-side clocks if necessary. */
  236.     if (g_rt_per_side() > 0) {
  237.     map->sidespacing += fh;
  238.     }
  239.     /* Add room to display scorekeepers if necessary. */
  240.     if (keeping_score()) {
  241.     map->sidespacing += ((numscorekeepers + 1) / 2) * fh;
  242.     }
  243.     subdivide_map(side, map);
  244.  
  245.     if (power < 0) {
  246.     /* We must want a world map; compute a power that will
  247.        be a good fit. */
  248.     for (power = NUMPOWERS - 2; power > 0; --power) {
  249.         if (hws[power] * area.width < map->pxw
  250.         && hcs[power] * area.height < map->pxh
  251.         && hws[power+1] * area.width >= map->pxw
  252.         && hcs[power+1] * area.height >= map->pxh)
  253.           break;
  254.     }
  255.     }
  256.     map->vp = new_vp();
  257.     set_map_power(side, map, power);
  258.  
  259.     set_view_size(map->vp, map->pxw, map->pxh);
  260.     pick_a_focus(side, &x, &y);
  261.     set_view_focus(map->vp, x, y);
  262.     /* Set default values for the display controls. */
  263.     map->drawterrain = TRUE;
  264.     map->drawgrid = TRUE;  /* should get from a resource */
  265.     map->drawcellpats = side->ui->monochrome;
  266.     map->drawunits = TRUE;
  267.     map->drawnames = FALSE /* (map->hh >= 8) */;
  268.     map->drawpeople = FALSE;
  269.     map->drawelevations = FALSE;
  270.     map->drawfeatureboundaries = FALSE;
  271.     map->drawfeaturenames = FALSE;
  272.     /* cache the highest feature number; don't forget to update this 
  273.        if new featured are added (presently not implemented)  */
  274.     side->ui->numfeatures = num_features();
  275.     place_legends(side);
  276.     for_all_material_types(m)
  277.       map->drawresources[m] = FALSE;
  278.     map->drawtemp = FALSE;
  279.     map->drawweather = FALSE;
  280.     map->seeall = g_see_all();
  281.     /* Start the map off in generic move mode normally. */
  282.     map->curtool = movetool;
  283.     map->inpch = '\0';
  284.     map->inptype = NONUTYPE;
  285.     /* Make the unit type string be empty. */
  286.     map->ustr[0] = '\0';
  287.     /* Newest map goes on the front of the list. */
  288.     map->next = side->ui->maps;
  289.     side->ui->maps = map;
  290.  
  291.     if (map->next == NULL) {
  292.     shell = side->ui->shell;
  293.     } else {
  294.     shell = XtVaCreatePopupShell("Map", topLevelShellWidgetClass,
  295.                      side->ui->shell,                   
  296.                      XtNwidth, map->totalw,
  297.                      XtNheight, map->totalh,
  298.                      NULL);
  299. /*    XSetWMProtocols(dpy, XtWindow(shell), &side->ui->kill_atom, 1);  */
  300.     }
  301.  
  302.     /* Build up the complex of widgets that constitute the map window. */
  303.     map->mainwidget =
  304.       XtVaCreateManagedWidget("Map", panedWidgetClass, shell,
  305.                   XtNwidth, map->totalw,
  306.                   XtNheight, map->totalh,
  307.                   XtNorientation, XtorientHorizontal,
  308.                   NULL);
  309.     
  310.     map->leftpane =
  311.       XtVaCreateManagedWidget("LeftPane", panedWidgetClass, map->mainwidget,
  312.                   XtNwidth, map->leftw,
  313.                   XtNmin, 50,
  314.                   NULL);
  315.  
  316.     map->infoform =
  317.       XtVaCreateManagedWidget("InfoForm", panedWidgetClass, map->leftpane,
  318.                   XtNmin, 80,
  319.                   NULL);
  320.  
  321.     map->history =
  322.       XtVaCreateManagedWidget("History", asciiTextWidgetClass, map->infoform,
  323.                   XtNscrollHorizontal, XawtextScrollWhenNeeded,
  324.                   XtNscrollVertical, XawtextScrollAlways,
  325.                   XtNshowGrip, False,
  326.                   NULL);
  327.  
  328.     /* It would be helpful sometimes to have a two-line input prompt window, but
  329.        it chews valuable space.  Perhaps only have two lines if map is "large". */
  330.     map->promptlabel =
  331.       XtVaCreateManagedWidget("Prompt", labelWidgetClass, map->infoform,
  332.                   XtNlabel, " ",
  333.                   XtNshowGrip, False,
  334.                   XtNskipAdjust, True,
  335.                   NULL);
  336.  
  337.     map->gamedate =
  338.       XtVaCreateManagedWidget("GameDate", labelWidgetClass, map->infoform,
  339.                   XtNlabel, " ",
  340.                   XtNborderWidth, 0,
  341.                   XtNshowGrip, False,
  342.                   XtNskipAdjust, True,
  343.                   NULL);
  344.    /* (should split gamedate and put clock on same line...) */
  345.    if (g_rt_per_turn() > 0 || g_rt_for_game() > 0)
  346.      map->gameclock =
  347.       XtVaCreateManagedWidget("GameClock", labelWidgetClass, map->infoform,
  348.                   XtNlabel, " ",
  349.                   XtNborderWidth, 0,
  350.                   XtNshowGrip, False,
  351.                   XtNskipAdjust, True,
  352.                   NULL);
  353. #if 0 /* this needs a home, but don't let it chew up screen space all the time */
  354.     map->msgarea =
  355.       XtVaCreateManagedWidget("Message", labelWidgetClass, map->infoform,
  356.                   /* Make it a two line label. */
  357.                   XtNlabel, "one\ntwo",
  358.                   XtNright, XtChainRight,
  359.                   XtNleft, XtChainLeft,
  360.                   XtNtop, XtChainBottom,
  361.                   XtNbottom, XtChainBottom,
  362.                   XtNfromVert, map->gameclock,
  363.                   NULL);
  364. #endif
  365.  
  366.     map->leftform =
  367.       XtVaCreateManagedWidget("LeftForm", panedWidgetClass, map->leftpane,
  368.                   XtNmin, 50,
  369.                   XtNorientation, XtorientHorizontal,
  370.                   NULL);
  371.  
  372.     map->controlform =
  373.       XtVaCreateManagedWidget("ControlForm", formWidgetClass, map->leftform,
  374.                   XtNmax, 150,
  375.                   XtNmin, 50,
  376.                   NULL);
  377.     XawFormDoLayout(map->controlform, False);
  378.     create_map_controls(side, map);
  379.     XawFormDoLayout(map->controlform, True);
  380.  
  381.     map->mapform =
  382.       XtVaCreateManagedWidget("MapForm", panedWidgetClass, map->leftform,
  383.                   XtNmin, 50,
  384.                   XtNorientation, XtorientVertical,
  385.                   NULL);
  386.  
  387.     map->info =
  388.       XtVaCreateManagedWidget("Info", widgetClass, map->mapform,
  389.                   XtNwidth, map->pxw,
  390.                   XtNheight, map->infoh,
  391.                   /* Insist that at least 2 lines be visible. */
  392.                   XtNmin, 2 * fh,
  393.                   /* ...might be useful to have large info window,
  394.                      so don't limit its max size. */
  395.                   NULL);
  396.     XtAddEventHandler(map->info,
  397.                   KeyPressMask|ButtonPressMask|ButtonReleaseMask
  398.               |ExposureMask|StructureNotifyMask,
  399.                   False, handle_map_info_events, NULL);
  400.  
  401.     map->porthole =
  402.       XtVaCreateManagedWidget("Porthole", portholeWidgetClass, map->mapform,
  403.                   XtNwidth, map->pxw,
  404.                   XtNheight, map->pxh,
  405.                   /* Don't let the map disappear completely. */
  406.                   XtNmin, 50,
  407.                   NULL);
  408.     XtAddCallback(map->porthole, XtNreportCallback,
  409.           porthole_callback, NULL);
  410.     map->portlabel =
  411.       XtVaCreateManagedWidget("PortholeLabel", labelWidgetClass, map->porthole,
  412.                   XtNinternalHeight, 0,
  413.                   XtNinternalWidth, 0,
  414.                   XtNwidth, map->pxw,
  415.                   XtNheight, map->pxh,
  416.                   NULL);
  417.  
  418.     /* The right side of the window is dedicated to lists and to the
  419.        map panner. */
  420.  
  421.     map->rightpane =
  422.       XtVaCreateManagedWidget("RightPane", panedWidgetClass, map->mainwidget,
  423.                   /* Let this side become small but not vanishingly small. */
  424.                   XtNmin, 50,
  425.                   NULL);
  426.  
  427.     map->sides =
  428.       XtVaCreateManagedWidget("Sides", widgetClass, map->rightpane,
  429.                   XtNwidth, map->list1w,
  430.                   XtNheight, map->list1h,
  431.                   /* At least one side should be visible always. */
  432.                   XtNmin, map->sidespacing,
  433.                   /* No reason to grow to more than numsides. */
  434.                   XtNmax, numsides * map->sidespacing,
  435.                   NULL);
  436.     XtAddEventHandler(map->sides,
  437.                   KeyPressMask|ButtonPressMask|ButtonReleaseMask
  438.               |ExposureMask|StructureNotifyMask,
  439.                   False, handle_map_sides_events, NULL);
  440.  
  441.     map->listview =
  442.       XtVaCreateManagedWidget("ListView", viewportWidgetClass, map->rightpane,
  443.                   XtNallowVert, True,
  444.                   NULL);
  445.     map->listform =
  446.       XtVaCreateManagedWidget("ListForm", formWidgetClass, map->listview,
  447.                   NULL);
  448.  
  449.     map->list_buttons = (Widget *) xmalloc(sizeof (Widget) * (numutypes + 1));
  450.     for (i = 0; i <= numutypes; i++) {
  451.     pic = XCreatePixmap(dpy, side->ui->rootwin,
  452.                 min_w_for_unit_image, min_h_for_unit_image,
  453.                 DefaultDepth(dpy, side->ui->screen));
  454.     XSetForeground(dpy, side->ui->gc, side->ui->bgcolor);
  455.     XFillRectangle(dpy, pic, side->ui->gc,
  456.                0, 0, min_w_for_unit_image, min_h_for_unit_image);
  457.     XSetForeground(dpy, side->ui->gc, side->ui->fgcolor);
  458.     if (i == 0) {
  459.         map->list_buttons[i] =
  460.           XtVaCreateManagedWidget(NULL, commandWidgetClass, map->listform,
  461.                       XtNborderWidth, 0,
  462.                       XtNheight, min_h_for_unit_image + 4,
  463.                       XtNjustify, XtJustifyLeft,
  464.                       XtNlabel, "  Num(Bld)  Total  Lost",
  465.                       XtNleftBitmap, pic,
  466.                       XtNresize, False,
  467.                       NULL);
  468.         /* Don't do anything if mouse passes over. */
  469.         XtUninstallTranslations(map->list_buttons[i]);
  470.     } else {
  471.         /* Draw the unit image into the pixmap.
  472.            NOTE: The command widget expects the leftBitmap to be in
  473.            "bitmap" format - a 1 bit gets drawn in the foreground
  474.            color and a 0 bit in the background color.
  475.            That's why we pass in 1 & 0 to draw_unit_image() here for
  476.            fgcolor & bgcolor. */
  477.         draw_unit_image(side, pic, 0, 0,
  478.                 min_w_for_unit_image, min_h_for_unit_image,
  479.                 i - 1, -1, 1, 0);
  480.  
  481.         /* Name the widget. */
  482.         build_name(wbuff, "utype_", u_type_name(i - 1));
  483.         map->list_buttons[i] =
  484.           XtVaCreateManagedWidget(wbuff, commandWidgetClass, map->listform,
  485.                       /* 0 looks better, but use 1 for debugging. */
  486.                       XtNborderWidth, 1,
  487.                       XtNheight, min_h_for_unit_image + 4,
  488.                       XtNjustify, XtJustifyLeft,
  489.                       XtNlabel, "                       ",
  490.                       XtNleftBitmap, pic,
  491.                       XtNresize, False,
  492.                       /* Tie to next button up. */
  493.                       XtNfromVert, map->list_buttons[i - 1],
  494.                       NULL);
  495.         /* Don't do anything if mouse passes over. */
  496.         XtUninstallTranslations(map->list_buttons[i]);
  497.         XtAddCallback(map->list_buttons[i], XtNcallback,
  498.               unit_type_list_callback, (XtPointer) i);
  499.         update_unit_type_list(side, map, i - 1);
  500.     }
  501.     }
  502.     /* (should fix this calculation) */
  503.     sx = (map->panw * 100) / (map->vp->totsw - hexagon_adjust(map->vp));
  504.     sy = (map->panh * 100) / map->vp->totsh;
  505.     scale = min(sx, sy);
  506.  
  507.     map->pannerbox =
  508.       XtVaCreateManagedWidget("PannerBox", boxWidgetClass, map->rightpane,
  509.                   XtNmin, map->panh + 8,
  510.                   XtNmax, map->panh + 8,
  511.                   XtNshowGrip, False,
  512.                   XtNskipAdjust, True,
  513.                   NULL);
  514.     map->panner =
  515.       XtVaCreateManagedWidget("Panner", pannerWidgetClass, map->pannerbox,
  516.                   XtNwidth, map->panw,
  517.                   XtNheight, map->panh,
  518.                   XtNcanvasWidth, map->vp->totsw - hexagon_adjust(map->vp),
  519.                   XtNcanvasHeight, map->vp->totsh,
  520.                   XtNdefaultScale, scale,
  521.                   /* Don't let the panner's size change,
  522.                  should recalculate the scale instead. */
  523.                   XtNresize, False,
  524.                   XtNrubberBand, True,
  525.                   XtNsliderWidth, map->pxw,
  526.                   XtNsliderHeight, map->pxh,
  527.                   NULL);
  528.     XtAddCallback(map->panner, XtNreportCallback,
  529.           panner_callback, NULL);
  530.  
  531.     XtAddEventHandler(map->panner, StructureNotifyMask, False,
  532.               panner_resize_handler, NULL);
  533.  
  534.     XtRealizeWidget(shell);
  535.  
  536.     x_center_on_focus(side, map);
  537.     clear_prompt(side, map);
  538.  
  539.     /* Make the display appear. */
  540.     XtPopup(side->ui->shell, XtGrabNone);
  541.  
  542.     /* Set the cursor to match the current tool. */
  543.     set_tool_cursor(side, map);
  544.  
  545.     /* Cache some windows into their own slots, for convenience later. */
  546.     map->infowin = XtWindow(map->info);
  547.     map->viewwin = XtWindow(map->portlabel);
  548.     map->sideswin = XtWindow(map->sides);
  549.  
  550.     draw_map(side, map);
  551.  
  552.     return map;
  553. }
  554.  
  555. static void
  556. create_map_controls(side, map)
  557. Side *side;
  558. Map *map;
  559. {
  560.     int wid = 80;
  561.  
  562.     map->controls = (Widget *) xmalloc(numcontrols * sizeof(Widget));
  563.  
  564.     map->controls[LOOK] =
  565.       XtVaCreateManagedWidget("look", toggleWidgetClass, map->controlform,
  566.                   XtNlabel, "Look",
  567. #if 1 /* X11R5 or later */
  568.                   XtNleftBitmap, side->ui->controlpics[LOOK],
  569. #endif
  570.                   XtNresize, False,
  571.                   XtNwidth, wid,
  572.                   NULL);
  573.     XtAddCallback(map->controls[LOOK], XtNcallback,
  574.           mode_callback, (XtPointer) LOOK);
  575.     map->controls[MOVE] =
  576.       XtVaCreateManagedWidget("move", toggleWidgetClass, map->controlform,
  577.                   XtNlabel, "Move",
  578. #if 1 /* X11R5 or later */
  579.                   XtNleftBitmap, side->ui->controlpics[MOVE],
  580. #endif
  581.                   XtNresize, False,
  582.                   XtNwidth, wid,
  583.                   XtNfromVert, map->controls[LOOK],
  584.                   NULL);
  585.     XtAddCallback(map->controls[MOVE], XtNcallback,
  586.           mode_callback, (XtPointer) MOVE);
  587.  
  588.     map->controls[UNIT_MOVE] =
  589.       XtVaCreateManagedWidget("unitmove", commandWidgetClass, map->controlform,
  590.                   XtNlabel, "Move To",
  591. #if 1 /* X11R5 or later */
  592.                   XtNleftBitmap, side->ui->controlpics[UNIT_MOVE],
  593. #endif
  594.                   XtNresize, False,
  595.                   XtNwidth, wid,
  596.                   XtNfromVert, map->controls[MOVE],
  597.                   XtNvertDistance, 10,
  598.                   NULL);
  599.     XtAddCallback(map->controls[UNIT_MOVE], XtNcallback,
  600.           command_callback, (XtPointer) UNIT_MOVE);
  601.     map->controls[UNIT_SHOOT] =
  602.       XtVaCreateManagedWidget("unitshoot", commandWidgetClass, map->controlform,
  603.                   XtNlabel, "Fire",
  604. #if 1 /* X11R5 or later */
  605.                   XtNleftBitmap, side->ui->controlpics[UNIT_SHOOT],
  606. #endif
  607.                   XtNresize, False,
  608.                   XtNwidth, wid,
  609.                   XtNfromVert, map->controls[UNIT_MOVE],
  610.                   NULL);
  611.     XtAddCallback(map->controls[UNIT_SHOOT], XtNcallback,
  612.           command_callback, (XtPointer) UNIT_SHOOT);
  613.     map->controls[UNIT_BUILD] =
  614.       XtVaCreateManagedWidget("unitbuild", commandWidgetClass, map->controlform,
  615.                   XtNlabel, "Build",
  616. #if 1 /* X11R5 or later */
  617.                   XtNleftBitmap, side->ui->controlpics[UNIT_BUILD],
  618. #endif
  619.                   XtNresize, False,
  620.                   XtNwidth, wid,
  621.                   XtNfromVert, map->controls[UNIT_SHOOT],
  622.                   NULL);
  623.     XtAddCallback(map->controls[UNIT_BUILD], XtNcallback,
  624.           command_callback, (XtPointer) UNIT_BUILD);
  625.  
  626.     map->controls[SHOW_TERRAIN] =
  627.       XtVaCreateManagedWidget("terrain", toggleWidgetClass, map->controlform,
  628.                   XtNlabel, "Terrain",
  629.                   XtNresize, False,
  630.                   XtNwidth, wid,
  631.                   XtNfromVert, map->controls[UNIT_BUILD],
  632.                   XtNvertDistance, 10,
  633.                   NULL);
  634.     XtAddCallback(map->controls[SHOW_TERRAIN], XtNcallback,
  635.           view_flag_callback, (XtPointer) SHOW_TERRAIN);
  636.     map->controls[SHOW_GRID] =
  637.       XtVaCreateManagedWidget("grid", toggleWidgetClass, map->controlform,
  638.                   XtNlabel, "Grid",
  639.                   XtNresize, False,
  640.                   XtNwidth, wid,
  641.                   XtNfromVert, map->controls[SHOW_TERRAIN],
  642.                   NULL);
  643.     XtAddCallback(map->controls[SHOW_GRID], XtNcallback,
  644.           view_flag_callback, (XtPointer) SHOW_GRID);
  645.     map->controls[SHOW_UNITS] =
  646.       XtVaCreateManagedWidget("units", toggleWidgetClass, map->controlform,
  647.                   XtNlabel, "Units",
  648.                   XtNresize, False,
  649.                   XtNwidth, wid,
  650.                   XtNfromVert, map->controls[SHOW_GRID],
  651.                   NULL);
  652.     XtAddCallback(map->controls[SHOW_UNITS], XtNcallback,
  653.           view_flag_callback, (XtPointer) SHOW_UNITS);
  654.     map->controls[SHOW_NAMES] =
  655.       XtVaCreateManagedWidget("names", toggleWidgetClass, map->controlform,
  656.                   XtNlabel, "Names",
  657.                   XtNresize, False,
  658.                   XtNwidth, wid,
  659.                   XtNfromVert, map->controls[SHOW_UNITS],
  660.                   NULL);
  661.     XtAddCallback(map->controls[SHOW_NAMES], XtNcallback,
  662.           view_flag_callback, (XtPointer) SHOW_NAMES);
  663.     map->controls[SHOW_PEOPLE] =
  664.       XtVaCreateManagedWidget("people", toggleWidgetClass, map->controlform,
  665.                   XtNlabel, "People",
  666.                   XtNresize, False,
  667.                   XtNwidth, wid,
  668.                   XtNfromVert, map->controls[SHOW_NAMES],
  669.                   NULL);
  670.     XtAddCallback(map->controls[SHOW_PEOPLE], XtNcallback,
  671.           view_flag_callback, (XtPointer) SHOW_PEOPLE);
  672.     map->controls[SHOW_ALL] =
  673.       XtVaCreateManagedWidget("all", toggleWidgetClass, map->controlform,
  674.                   XtNlabel, "All",
  675.                   XtNresize, False,
  676.                   XtNsensitive, !g_see_all(),
  677.                   XtNwidth, wid,
  678.                   XtNfromVert, map->controls[SHOW_PEOPLE],
  679.                   NULL);
  680.     XtAddCallback(map->controls[SHOW_ALL], XtNcallback,
  681.           view_flag_callback, (XtPointer) SHOW_ALL);
  682.     map->controls[SHOW_MORE] =
  683.       XtVaCreateManagedWidget("more", toggleWidgetClass, map->controlform,
  684.                   XtNlabel, "More...",
  685.                   XtNresize, False,
  686.                   XtNwidth, wid,
  687.                   XtNfromVert, map->controls[SHOW_ALL],
  688.                   NULL);
  689.     XtAddCallback(map->controls[SHOW_MORE], XtNcallback,
  690.           view_flag_callback, (XtPointer) SHOW_MORE);
  691.  
  692.     map->controls[ZOOM_OUT] =
  693.       XtVaCreateManagedWidget("zoomout", commandWidgetClass, map->controlform,
  694.                   XtNbitmap, side->ui->controlpics[ZOOM_OUT],
  695.                   XtNresize, False,
  696.                   XtNwidth, wid / 2 - 10,
  697.                   XtNfromVert, map->controls[SHOW_MORE],
  698.                   XtNvertDistance, 10,
  699.                   NULL);
  700.     XtAddCallback(map->controls[ZOOM_OUT], XtNcallback,
  701.           zoom_callback, (XtPointer) ZOOM_OUT);
  702.     map->controls[ZOOM_IN] =
  703.       XtVaCreateManagedWidget("zoomin", commandWidgetClass, map->controlform,
  704.                   XtNbitmap, side->ui->controlpics[ZOOM_IN],
  705.                   XtNresize, False,
  706.                   XtNwidth, wid / 2 - 10,
  707.                   XtNfromVert, map->controls[SHOW_MORE],
  708.                   XtNfromHoriz, map->controls[ZOOM_OUT],
  709.                   XtNvertDistance, 10,
  710.                   NULL);
  711.     XtAddCallback(map->controls[ZOOM_IN], XtNcallback,
  712.           zoom_callback, (XtPointer) ZOOM_IN);
  713.  
  714.     update_controls(side, map);
  715. }
  716.  
  717. void
  718. set_map_power(side, map, power)
  719. Side *side;
  720. Map *map;
  721. int power;
  722. {
  723.     set_view_power(map->vp, power);
  724.     if (map->panner) {
  725.     XtVaSetValues(map->panner,
  726.               XtNcanvasWidth, map->vp->totsw - hexagon_adjust(map->vp),
  727.               XtNcanvasHeight, map->vp->totsh,
  728.               /* what about slider size? */
  729.               NULL);
  730.     }
  731. }
  732.  
  733. void
  734. x_center_on_focus(side, map)
  735. Side *side;
  736. Map *map;
  737. {
  738.     center_on_focus(map->vp);
  739.     if (map->panner) {
  740.     XtVaSetValues(map->panner,
  741.               XtNsliderX, map->vp->sx - hexagon_adjust(map->vp),
  742.               XtNsliderY, map->vp->sy,
  743.               NULL);
  744.     }
  745. }
  746.  
  747. static void
  748. MA_setcoord(w, event, params, num_params)
  749. Widget w;
  750. XEvent *event;
  751. String *params;
  752. Cardinal *num_params;
  753. {
  754.     int cellx, celly;
  755.     Side *side;
  756.     Map *map;
  757.     XButtonEvent *btn = &event->xbutton;
  758.  
  759.     if (find_side_and_map_via_porthole(w, &side, &map)) {
  760.     side->ui->mapdown = map;
  761.     side->ui->sxdown = btn->x;  side->ui->sydown = btn->y;
  762.     side->ui->cellxy_ok = x_nearest_cell(side, map, btn->x, btn->y, &cellx, &celly);
  763.     if (side->ui->cellxy_ok) {
  764.         side->ui->cellx = cellx;  side->ui->celly = celly;
  765.     }
  766.     }
  767. }
  768.  
  769. static void
  770. MA_motionsetcoord(w, event, params, num_params)
  771. Widget w;
  772. XEvent *event;
  773. String *params;
  774. Cardinal *num_params;
  775. {
  776.     int cellx, celly;
  777.     Side *side;
  778.     Map *map;
  779.     XMotionEvent *mtn = &event->xmotion;
  780.  
  781.     if (find_side_and_map_via_porthole(w, &side, &map)) {
  782.     side->ui->mapdown = map;
  783.     side->ui->sxdown = mtn->x;  side->ui->sydown = mtn->y;
  784.     side->ui->cellxy_ok = x_nearest_cell(side, map, mtn->x, mtn->y, &cellx, &celly);
  785.     if (side->ui->cellxy_ok) {
  786.         side->ui->cellx = cellx;  side->ui->celly = celly;
  787.     }
  788.     }
  789. }
  790.  
  791. static void
  792. MA_movelook(w, event, params, num_params)
  793. Widget w;
  794. XEvent *event;
  795. String *params;
  796. Cardinal *num_params;
  797. {
  798.     int cellx, celly;
  799.     Unit *unit;
  800.     Side *side;
  801.     Map *map;
  802.  
  803.     if (!find_side_and_map_via_porthole(w, &side, &map))
  804.       return;
  805.  
  806.     if (side->ui->cellxy_ok) {
  807.     cellx = side->ui->cellx;  celly = side->ui->celly;
  808.     if (inside_area(cellx, celly)) {
  809.         x_nearest_unit(side, map, side->ui->sxdown, side->ui->sydown, &unit);
  810.         if (unit != NULL &&
  811.         (side_controls_unit(side, unit) || map->seeall)) {
  812.         set_current_unit(side, map, unit);
  813.         } else {
  814.         set_current_xy(side, map, cellx, celly);
  815.         }
  816.     } else {
  817.         beep(side);
  818.     }
  819.     }
  820. }
  821.  
  822. static void
  823. MA_movelook_ul(w, event, params, num_params)
  824. Widget w;
  825. XEvent *event;
  826. String *params;
  827. Cardinal *num_params;
  828. {
  829.     movelook_one_cell(w, dirx[NORTHWEST], diry[NORTHWEST]);
  830. }
  831.  
  832. static void
  833. MA_movelook_l(w, event, params, num_params)
  834. Widget w;
  835. XEvent *event;
  836. String *params;
  837. Cardinal *num_params;
  838. {
  839.     movelook_one_cell(w, dirx[WEST], diry[WEST]);
  840. }
  841.  
  842. static void
  843. MA_movelook_dl(w, event, params, num_params)
  844. Widget w;
  845. XEvent *event;
  846. String *params;
  847. Cardinal *num_params;
  848. {
  849.     movelook_one_cell(w, dirx[SOUTHWEST], diry[SOUTHWEST]);
  850. }
  851.  
  852. static void
  853. MA_movelook_ur(w, event, params, num_params)
  854. Widget w;
  855. XEvent *event;
  856. String *params;
  857. Cardinal *num_params;
  858. {
  859.     movelook_one_cell(w, dirx[NORTHEAST], diry[NORTHEAST]);
  860. }
  861.  
  862. static void
  863. MA_movelook_r(w, event, params, num_params)
  864. Widget w;
  865. XEvent *event;
  866. String *params;
  867. Cardinal *num_params;
  868. {
  869.     movelook_one_cell(w, dirx[EAST], diry[EAST]);
  870. }
  871.  
  872. static void
  873. MA_movelook_dr(w, event, params, num_params)
  874. Widget w;
  875. XEvent *event;
  876. String *params;
  877. Cardinal *num_params;
  878. {
  879.     movelook_one_cell(w, dirx[SOUTHEAST], diry[SOUTHEAST]);
  880. }
  881.  
  882. static void
  883. movelook_one_cell(w, xdelta, ydelta)
  884. Widget w;
  885. int xdelta, ydelta;
  886. {
  887.     Side *side;
  888.     Map *map;
  889.     Unit *unit;
  890.     int nx, ny;
  891.  
  892.     if (!find_side_and_map_via_porthole(w, &side, &map))
  893.       return;
  894.  
  895.     nx = map->curx + xdelta;  ny = map->cury + ydelta;
  896.     if (inside_area(nx, ny)) {
  897.     unit = unit_at(nx, ny);
  898.     if (unit != NULL &&
  899.         (side_controls_unit(side, unit) || map->seeall)) {
  900.         set_current_unit(side, map, unit);
  901.     } else {
  902.         set_current_xy(side, map, nx, ny);
  903.     }
  904.     } else {
  905.     beep(side);
  906.     }
  907. }
  908.  
  909. static void
  910. MA_center(w, event, params, num_params)
  911. Widget w;
  912. XEvent *event;
  913. String *params;
  914. Cardinal *num_params;
  915. {
  916.     Side *side;
  917.     Map *map;
  918.  
  919.     if (!find_side_and_map_via_porthole(w, &side, &map))
  920.       return;
  921.  
  922.     recenter(side, map, map->curx, map->cury);
  923. }
  924.  
  925. static void
  926. MA_message(w, event, params, num_params)
  927. Widget w;
  928. XEvent *event;
  929. String *params;
  930. Cardinal *num_params;
  931. {
  932.     Side *side;
  933.     Map *map;
  934.     String msg;
  935.     Cardinal argc, nbytes, nparams;
  936.  
  937.     if (!find_side_and_map_via_control(w, &side, &map)
  938.     && !find_side_and_map_via_ctrlpanel_form(XtParent(w), &side, &map))
  939.       return;
  940.  
  941.     /* How many bytes of message? */
  942.     nbytes = 0;
  943.     nparams = *num_params;
  944.     for (argc = 0; argc < nparams; argc++) {
  945.     nbytes += strlen(params[argc]);
  946.     }
  947.     /* And some for the white space... */
  948.     nbytes += (*num_params - 1);
  949.     if (nbytes <= 0) {
  950.     set_message_area(map, "");
  951.     } else {
  952.     /* Don't forget one for the zero byte! */
  953.     nbytes++;
  954.     msg = (String) XtCalloc(nbytes, sizeof (char));
  955.     for (argc = 0; argc < nparams; argc++) {
  956.         (void) strcat(msg, params[argc]);
  957.         if (argc < (nparams-1))
  958.           (void) strcat(msg, " ");
  959.     }
  960.     set_message_area(map, msg);
  961.     XtFree(msg);
  962.     }
  963. }
  964.  
  965. static void
  966. MA_moveunit(w, event, params, num_params)
  967. Widget w;
  968. XEvent *event;
  969. String *params;
  970. Cardinal *num_params;
  971. {
  972.     Side *side;
  973.     Map *map;
  974.     Unit *unit;
  975.     int cellx, celly;
  976.  
  977.     if (!find_side_and_map_via_porthole(w, &side, &map))
  978.       return;
  979.  
  980.     if (side->ui->cellxy_ok) {
  981.     if (map->curunit && side_controls_unit(side, map->curunit)) {
  982.         move_the_selected_unit(side, map, map->curunit,
  983.                    side->ui->sxdown, side->ui->sydown);
  984.     } else {
  985.         beep(side);
  986.     }
  987.     }
  988. }
  989.  
  990. static void
  991. MA_distance(w, event, params, num_params)
  992. Widget w;
  993. XEvent *event;
  994. String *params;
  995. Cardinal *num_params;
  996. {
  997.     Side *side;
  998.     Map *map;
  999.     XButtonEvent *btn = &event->xbutton;
  1000.     int upcellx, upcelly;
  1001.     int dist;
  1002.  
  1003.     if (!find_side_and_map_via_porthole(w, &side, &map))
  1004.       return;
  1005.     if (side->ui->cellxy_ok) {
  1006.     if (x_nearest_cell(side, map, btn->x, btn->y, &upcellx, &upcelly)) {
  1007.         dist = distance(side->ui->cellx, side->ui->celly, upcellx, upcelly);
  1008.         notify(side, "The distance is %d cells.", dist);
  1009.     } else {
  1010.         beep(side);
  1011.     }
  1012.     }
  1013. }
  1014.  
  1015. static void
  1016. MA_keypress(w, event, params, num_params)
  1017. Widget w;
  1018. XEvent *event;
  1019. String *params;
  1020. Cardinal *num_params;
  1021. {
  1022.     Side *side;
  1023.     Map *map;
  1024.     XKeyEvent *key = &event->xkey;
  1025.  
  1026.     if (!find_side_and_map_via_porthole(w, &side, &map))
  1027.       return;
  1028.  
  1029.     side->ui->sxdown = key->x;  side->ui->sydown = key->y;
  1030.     side->ui->mapdown = map;
  1031.     handle_key_event(side, map, event);
  1032. }
  1033.  
  1034. static void
  1035. MA_mapexpose(w, event, params, num_params)
  1036. Widget w;
  1037. XEvent *event;
  1038. String *params;
  1039. Cardinal *num_params;
  1040. {
  1041.     Side *side;
  1042.     Map *map;
  1043.     XKeyEvent *key = &event->xkey;
  1044.     XEvent qevent;
  1045.  
  1046.     if (!find_side_and_map_via_porthole(w, &side, &map))
  1047.       return;
  1048.  
  1049.     /* Do nothing if more expose events are following. */
  1050.     if (event->type == Expose && event->xexpose.count > 0)
  1051.       return;
  1052.  
  1053.     /* Clear the queue of other Expose event directed to viewwin. */
  1054.     while (XCheckTypedWindowEvent(side->ui->dpy, map->viewwin,
  1055.                   Expose, &qevent))
  1056.       ;
  1057.  
  1058.     draw_map(side, map);
  1059. }
  1060.  
  1061. static void
  1062. MA_toolaction(w, event, params, num_params)
  1063. Widget w;
  1064. XEvent *event;
  1065. String *params;
  1066. Cardinal *num_params;
  1067. {
  1068.     Side *side;
  1069.     Map *map;
  1070.     XButtonEvent *btn = &event->xbutton;
  1071.     
  1072.     if (!find_side_and_map_via_porthole(w, &side, &map))
  1073.       return;
  1074.  
  1075.     handle_map_click(side, map, btn->x, btn->y);
  1076. }
  1077.  
  1078. /* Set the current designer paint/add/etc type from what's under the cursor. */
  1079.  
  1080. static void
  1081. MA_toolselection(w, event, params, num_params)
  1082. Widget w;
  1083. XEvent *event;
  1084. String *params;
  1085. Cardinal *num_params;
  1086. {
  1087.     Side *side;
  1088.     Map *map;
  1089.     XButtonEvent *btn = &event->xbutton;
  1090.  
  1091.     if (!find_side_and_map_via_porthole(w, &side, &map))
  1092.       return;
  1093.  
  1094. #ifdef DESIGNERS
  1095.     set_designer_cur_from_map(side, map, btn->x, btn->y);
  1096. #else
  1097.     beep(side);
  1098. #endif /* DESIGNERS */
  1099. }
  1100.  
  1101. int
  1102. find_side_and_map_via_control(w, sidep, mapp)
  1103. Widget w;
  1104. Side **sidep;
  1105. Map **mapp;
  1106. {
  1107.     Side *side;
  1108.     Map *map;
  1109.  
  1110.     for_all_sides(side) {
  1111.     if (active_display(side)) {
  1112.         if (XtDisplay(w) == side->ui->dpy) {
  1113.         for_all_maps(side, map) {
  1114.             if (map->controlform == XtParent(w)) {
  1115.             *sidep = side;
  1116.             *mapp = map;
  1117.             return TRUE;
  1118.             }
  1119.         }
  1120.         }
  1121.     }
  1122.     }
  1123.     return FALSE;
  1124. }
  1125.  
  1126. int
  1127. find_side_and_map_via_a_toplevel(w, sidep, mapp)
  1128. Widget w;
  1129. Side **sidep;
  1130. Map **mapp;
  1131. {
  1132.     Side *side;
  1133.     Map *map;
  1134.  
  1135.     for_all_sides(side) {
  1136.     if (active_display(side)) {
  1137.         if (XtDisplay(w) == side->ui->dpy) {
  1138.         for_all_maps(side, map) {
  1139.             if (side->ui->shell == w) {
  1140.             *sidep = side;
  1141.             *mapp = map;
  1142.             return TRUE;
  1143.             }
  1144.             if (side->ui->help_shell == w) {
  1145.             *sidep = side;
  1146.             *mapp = map;
  1147.             return TRUE;
  1148.             }
  1149. #ifdef DESIGNERS
  1150.             if (side->ui->design_shell == w) {
  1151.             *sidep = side;
  1152.             *mapp = map;
  1153.             return TRUE;
  1154.             }
  1155. #endif /* DESIGNERS */
  1156.         }
  1157.         }
  1158.     }
  1159.     }
  1160.     return FALSE;
  1161. }
  1162.  
  1163. int
  1164. find_side_and_map_via_listform(w, sidep, mapp)
  1165. Widget w;
  1166. Side **sidep;
  1167. Map **mapp;
  1168. {
  1169.     Side *side;
  1170.     Map *map;
  1171.  
  1172.     for_all_sides(side) {
  1173.     if (active_display(side)) {
  1174.         if (XtDisplay(w) == side->ui->dpy) {
  1175.         for_all_maps(side, map) {
  1176.             if (map->listform == XtParent(w)) {
  1177.             *sidep = side;
  1178.             *mapp = map;
  1179.             return TRUE;
  1180.             }
  1181.         }
  1182.         }
  1183.     }
  1184.     }
  1185.     return FALSE;
  1186. }
  1187.  
  1188. int
  1189. find_side_and_map_via_mapform(w, sidep, mapp)
  1190. Widget w;
  1191. Side **sidep;
  1192. Map **mapp;
  1193. {
  1194.     Side *side;
  1195.     Map *map;
  1196.  
  1197.     for_all_sides(side) {
  1198.     if (active_display(side)) {
  1199.         if (XtDisplay(w) == side->ui->dpy) {
  1200.         for_all_maps(side, map) {
  1201.             if (map->mapform == XtParent(w)) {
  1202.             *sidep = side;
  1203.             *mapp = map;
  1204.             return TRUE;
  1205.             }
  1206.         }
  1207.         }
  1208.     }
  1209.     }
  1210.     return FALSE;
  1211. }
  1212.  
  1213. int
  1214. find_side_and_map_via_porthole(w, sidep, mapp)
  1215. Widget w;
  1216. Side **sidep;
  1217. Map **mapp;
  1218. {
  1219.     Side *side;
  1220.     Map *map;
  1221.  
  1222.     for_all_sides(side) {
  1223.     if (active_display(side)) {
  1224.         if (XtDisplay(w) == side->ui->dpy) {
  1225.         for_all_maps(side, map) {
  1226.             if (map->porthole == XtParent(w)) {
  1227.             *sidep = side;
  1228.             *mapp = map;
  1229.             return TRUE;
  1230.             }
  1231.         }
  1232.         }
  1233.     }
  1234.     }
  1235.     return FALSE;
  1236. }
  1237.  
  1238. int
  1239. find_side_and_map_via_rightform(w, sidep, mapp)
  1240. Widget w;
  1241. Side **sidep;
  1242. Map **mapp;
  1243. {
  1244.     Side *side;
  1245.     Map *map;
  1246.  
  1247.     for_all_sides(side) {
  1248.     if (active_display(side)) {
  1249.         if (XtDisplay(w) == side->ui->dpy) {
  1250.         for_all_maps(side, map) {
  1251.             if (map->rightpane == XtParent(w)
  1252.                 || map->rightpane == XtParent(XtParent(w))) {
  1253.             *sidep = side;
  1254.             *mapp = map;
  1255.             return TRUE;
  1256.             }
  1257.         }
  1258.         }
  1259.     }
  1260.     }
  1261.     return FALSE;
  1262. }
  1263.  
  1264. /* Interpret a keyboard event, either as a single-key command or as
  1265.    part of some modal interaction. */
  1266.  
  1267. void
  1268. handle_key_event(side, map, evt)
  1269. Side *side;
  1270. Map *map;
  1271. XEvent *evt;
  1272. {
  1273.     char buf[4], ch;
  1274.     int nchar, cancelled;
  1275.     void (*fn) PROTO ((Side *sidex, Map *mapx, int cancelledx));
  1276.  
  1277.     nchar = XLookupString(&(evt->xkey), buf, 4, NULL, NULL);
  1278.     if (nchar > 0) {
  1279.     /* Collect only the first char of a possible sequence. */
  1280.     ch = buf[0];
  1281.     DGprintf("%s typed char '%c'\n", side_desig(side), ch);
  1282.     /* so we can do ^L even while typing a string */
  1283.     if (ch == REDRAW_CHAR) {
  1284.         redraw(side);
  1285.     } else if (map != NULL) {
  1286.         /* Save the char, we'll need it. */
  1287.         map->inpch = ch;
  1288.         /* Call the modal handler if defined, giving it side
  1289.            and cancel flag. */
  1290.         if (map->modalhandler) {
  1291.         fn = map->modalhandler;
  1292.         cancelled = (ch == ESCAPE_CHAR);
  1293.         /* Remove the handler - will restore itself if needed. */
  1294.         map->modalhandler = NULL;
  1295.         (*fn)(side, map, cancelled);
  1296.         if (cancelled) {
  1297.             /* Clean up any leftover interaction. */
  1298.             clear_prompt(side, map);
  1299.             notify(side, "Cancelled.");
  1300.         }
  1301.         } else {
  1302.         interp_key_command(side, map);
  1303.         }
  1304.     } else {
  1305.         beep(side);
  1306.     }
  1307.     } else {
  1308.     /* pretty strange, ok to do nothing? */
  1309.     }
  1310. }
  1311.  
  1312. static void
  1313. interp_key_command(side, map)
  1314. Side *side;
  1315. Map *map;
  1316. {
  1317.     DGprintf("%s keyboard input: %c (%d)\n",
  1318.          side_desig(side), map->inpch, map->argint);
  1319.     if (isdigit(map->inpch)) {
  1320.     if (map->argint < 0) {
  1321.         map->argint = 0;
  1322.     } else {
  1323.         map->argint *= 10;
  1324.     }
  1325.     map->argint += (map->inpch - '0');
  1326.         sprintf(map->prompt, "%d:", map->argint);
  1327.     sprintf(map->answer, "");
  1328.     draw_prompt(side, map);
  1329.     return;
  1330.     } else if (map->inpch == BACKSPACE_CHAR || map->inpch == DELETE_CHAR) {
  1331.     beep(side);
  1332.     } else if (map->inpch == ESCAPE_CHAR) {
  1333.     map->argint = -1;
  1334.     clear_prompt(side, map);
  1335.     } else {
  1336.     clear_prompt(side, map);
  1337.     execute_command(side, map);
  1338.     /* Reset the argument for next time. */
  1339.     map->argint = -1;
  1340.     }
  1341. }
  1342.  
  1343. void
  1344. handle_map_sides_events (w, clientdata, evt, contdispatch)
  1345. Widget w;
  1346. XtPointer clientdata;
  1347. XEvent *evt;
  1348. Boolean *contdispatch;
  1349. {
  1350.     int s;
  1351.     Side *side;
  1352.     Map *map;
  1353.  
  1354.     if (find_side_and_map_via_rightform(w, &side, &map)) {
  1355.     switch (evt->type) {
  1356.       case KeyPress:
  1357.         handle_key_event(side, map, evt);
  1358.         break;
  1359.       case ButtonPress:
  1360.         s = evt->xbutton.y / (side->ui->fh + 12) + 1;
  1361.         if (s < 0)
  1362.           s = 0;
  1363.         if (s > numsides) 
  1364.           s = numsides;
  1365. #ifdef DESIGNERS
  1366.         if (side->designer && side->ui->design_shell) {
  1367.         side->ui->cursidenumber = s;
  1368.         update_cursidenumber(side);
  1369.         }
  1370. #endif /* DESIGNERS */
  1371.         break;
  1372.       case Expose:
  1373.         draw_map_sides(side, map);
  1374.         break;
  1375.       default:
  1376.         DGprintf("Unhandled X event type %d, ignoring\n", evt->type);
  1377.         break;
  1378.     }
  1379.     }
  1380. }
  1381.  
  1382. void
  1383. handle_map_info_events (w, clientdata, evt, contdispatch)
  1384. Widget w;
  1385. XtPointer clientdata;
  1386. XEvent *evt;
  1387. Boolean *contdispatch;
  1388. {
  1389.     Side *side;
  1390.     Map *map;
  1391.  
  1392.     if (find_side_and_map_via_mapform(w, &side, &map)) {
  1393.     switch (evt->type) {
  1394.       case KeyPress:
  1395.         handle_key_event(side, map, evt);
  1396.         break;
  1397.       case ButtonPress:
  1398.         beep(side);
  1399.         break;
  1400.       case Expose:
  1401.         draw_map_info(side, map);
  1402.         break;
  1403.       default:
  1404.         DGprintf("Unhandled X event type %d, ignoring\n", evt->type);
  1405.         break;
  1406.     }
  1407.     }
  1408. }
  1409.  
  1410. /* Prompt for a type of a unit from player, maybe only allowing some types
  1411.    to be accepted.  Also allow specification of no unit type.  We do this
  1412.    by scanning the vector, building a string of chars and a vector of
  1413.    unit types, so as to be able to map back when done. */
  1414.  
  1415. int
  1416. ask_unit_type(side, map, prompt, possibles, handler)
  1417. Side *side;
  1418. Map *map;
  1419. char *prompt;
  1420. int *possibles;
  1421. void (*handler)();
  1422. {
  1423.     int u, numtypes = 0;
  1424.  
  1425.     for_all_unit_types(u) {
  1426.     if (possibles == NULL || possibles[u]) {
  1427.         map->uvec[numtypes] = u;
  1428.         map->ustr[numtypes] = utype_name_n(u, 1)[0];
  1429.         enable_in_unit_type_list(side, map, u, 1);
  1430.         ++numtypes;
  1431.     } else {
  1432.         enable_in_unit_type_list(side, map, u, -1);
  1433.     }
  1434.     }
  1435.     map->ustr[numtypes] = '\0';
  1436.     if (numtypes > 1) {
  1437.     sprintf(map->prompt, "%s [%s]", prompt, map->ustr);
  1438.     map->answer[0] = '\0';
  1439.     draw_prompt(side, map);
  1440.     map->modalhandler = handler;
  1441.     }
  1442.     return numtypes;
  1443. }
  1444.  
  1445. /* Do something with the char or unit type that the player entered. */
  1446.  
  1447. int
  1448. grok_unit_type(side, map, typep)
  1449. Side *side;
  1450. Map *map;
  1451. int *typep;
  1452. {
  1453.     int i, u;
  1454.  
  1455.     *typep = NONUTYPE;
  1456.     if (map->inptype != NONUTYPE) {
  1457.     *typep = map->inptype;
  1458.     } else if (map->inpch != '\0') {
  1459.     i = iindex(map->inpch, map->ustr);
  1460.     if (i >= 0) {
  1461.         *typep = map->uvec[i];
  1462.     } else {
  1463.         notify(side, "Must type a unit type char from the list, or <esc>");
  1464.         return FALSE;
  1465.     }
  1466.     } else {
  1467.     notify(side, "weird");
  1468.     return FALSE;
  1469.     }
  1470.     clear_prompt(side, map);
  1471.     /* Reset all the buttons in the unit type list. */
  1472.     for_all_unit_types(u) {
  1473.     enable_in_unit_type_list(side, map, u, 0);
  1474.     }
  1475.     /* Make the unit type string be empty. */
  1476.     map->ustr[0] = '\0';
  1477.     return TRUE;
  1478. }
  1479.  
  1480. void
  1481. enable_in_unit_type_list(side, map, u, flag)
  1482. Side *side;
  1483. Map *map;
  1484. int u, flag;
  1485. {
  1486.     XtVaSetValues(map->list_buttons[u + 1],
  1487.           XtNhighlightThickness, ((flag != 0) ? 2 : 0),
  1488.           XtNsensitive, ((flag >= 0) ? True : False),
  1489.           NULL);
  1490. }
  1491.  
  1492. int
  1493. ask_terrain_type(side, map, prompt, possibles, handler)
  1494. Side *side;
  1495. Map *map;
  1496. char *prompt;
  1497. int *possibles;
  1498. void (*handler) PROTO ((Side *side, Map *map, int cancelled));
  1499. {
  1500.     int numtypes = 0, t;
  1501.     char tcharbuf[MAXTTYPES+1];
  1502.  
  1503.     for_all_terrain_types(t) {
  1504.     if (possibles == NULL || possibles[t]) {
  1505.         map->tvec[numtypes] = t;
  1506.         map->tstr[numtypes] = (!empty_string(t_char(t)) ? t_char(t)[0] : (t - 'a'));
  1507.         ++numtypes;
  1508.     }
  1509.     }
  1510.     map->tstr[numtypes] = '\0';
  1511.     if (numtypes > 1) {
  1512.     sprintf(map->prompt, "%s [%s]", prompt, map->tstr);
  1513.     map->answer[0] = '\0';
  1514.     draw_prompt(side, map);
  1515.     map->modalhandler = handler;
  1516.     }
  1517.     return numtypes;
  1518. }
  1519.  
  1520. /* Do something with the char or terrain type that the player entered. */
  1521.  
  1522. int
  1523. grok_terrain_type(side, map, typep)
  1524. Side *side;
  1525. Map *map;
  1526. int *typep;
  1527. {
  1528.     int i;
  1529.  
  1530.     *typep = NONTTYPE;
  1531.     i = iindex(map->inpch, map->tstr);
  1532.     if (i >= 0) {
  1533.     *typep = map->tvec[i];
  1534.     clear_prompt(side, map);
  1535.     return TRUE;
  1536.     } else {
  1537.     notify(side, "Must type a terrain type char or <esc>");
  1538.     return FALSE;
  1539.     }
  1540. }
  1541.  
  1542. /* User is asked to pick a position on map.  This will iterate until the */
  1543. /* space bar designates the final position. */
  1544.  
  1545. /* (should change the cursor temporarily) */
  1546.  
  1547. void
  1548. ask_position(side, map, prompt, handler)
  1549. Side *side;
  1550. Map *map;
  1551. char *prompt;
  1552. void (*handler) PROTO ((Side *side, Map *map, int cancelled));
  1553. {
  1554.     sprintf(map->prompt, "%s [click to set]", prompt);
  1555.     map->answer[0] = '\0';
  1556.     draw_prompt(side, map);
  1557.     map->modalhandler = handler;
  1558. }
  1559.  
  1560. int
  1561. grok_position(side, map, xp, yp)
  1562. Side *side;
  1563. Map *map;
  1564. int *xp, *yp;
  1565. {
  1566.     
  1567.     if (in_area(map->curx, map->cury)) {
  1568.     *xp = map->curx;  *yp = map->cury;
  1569.     clear_prompt(side, map);
  1570.     return TRUE;
  1571.     } else {
  1572.     /* Make any possible usage attempts fail. */
  1573.     *xp = *yp = -1;
  1574.     return FALSE;
  1575.     }
  1576. }
  1577.  
  1578. /* Prompt for a yes/no answer with a settable default. */
  1579.  
  1580. void
  1581. ask_bool(side, map, question, dflt, handler)
  1582. Side *side;
  1583. Map *map;
  1584. char *question;
  1585. int dflt;
  1586. void (*handler) PROTO ((Side *side, Map *map, int cancelled));
  1587. {
  1588.     sprintf(map->prompt, "%s [%s]", question, (dflt ? "yn" : "ny"));
  1589.     map->answer[0] = '\0';
  1590.     draw_prompt(side, map);
  1591.     map->tmpint = dflt;
  1592.     map->modalhandler = handler;
  1593. }
  1594.  
  1595. /* Figure out what the answer actually is, keeping the default in mind. */
  1596.  
  1597. int
  1598. grok_bool(side, map)
  1599. Side *side;
  1600. Map *map;
  1601. {
  1602.     int dflt = map->tmpint;
  1603.     char ch = map->inpch;
  1604.  
  1605.     if (dflt ? (lowercase(ch) == 'n') : (lowercase(ch) == 'y'))
  1606.       dflt = !dflt;
  1607.     clear_prompt(side, map);
  1608.     return dflt;
  1609. }
  1610.  
  1611. /* Read a string from the prompt window.  Deletion is allowed, and a
  1612.    text cursor (an underscore) is displayed. */
  1613.  
  1614. void
  1615. ask_string(side, map, prompt, dflt, handler)
  1616. Side *side;
  1617. Map *map;
  1618. char *prompt, *dflt;
  1619. void (*handler) PROTO ((Side *side, Map *map, int cancelled));
  1620. {
  1621.     sprintf(map->prompt, "%s", prompt);
  1622.     /* Default must be non-NULL. */
  1623.     if (dflt == NULL)
  1624.       dflt = "";
  1625.     sprintf(map->answer, "%s", dflt);
  1626.     draw_prompt(side, map);
  1627.     map->modalhandler = handler;
  1628. }
  1629.  
  1630. /* Dig a character from the input and add it into the string.
  1631.    Keep returning FALSE until we get something, then make a copy
  1632.    of the result string and return TRUE. */
  1633.  
  1634. int
  1635. grok_string(side, map, strp)
  1636. Side *side;
  1637. Map *map;
  1638. char **strp;
  1639. {
  1640.     char ch = map->inpch;
  1641.     int len = strlen(map->answer);
  1642.  
  1643.     if (ch == '\r' || ch == '\n') {
  1644.     *strp = copy_string(map->answer);
  1645.     clear_prompt(side, map);
  1646.     return TRUE;
  1647.     } else {
  1648.     if (ch == BACKSPACE_CHAR || ch == DELETE_CHAR) {
  1649.         if (len > 0)
  1650.           --len;
  1651.     } else {
  1652.         map->answer[len++] = ch;
  1653.     }
  1654.     map->answer[len] = '\0';
  1655.     draw_prompt(side, map);
  1656.     return FALSE;
  1657.     }
  1658. }
  1659.  
  1660. void
  1661. ask_side(side, map, prompt, dflt, handler)
  1662. Side *side;
  1663. Map *map;
  1664. char *prompt;
  1665. Side *dflt;
  1666. void (*handler) PROTO ((Side *side, Map *map, int cancel));
  1667. {
  1668.     sprintf(map->prompt, "%s", prompt);
  1669.     sprintf(map->answer, "%s", side_desig(dflt));
  1670.     draw_prompt(side, map);
  1671.     map->modalhandler = handler;
  1672. }
  1673.  
  1674. int
  1675. grok_side(side, map, side2p)
  1676. Side *side;
  1677. Map *map;
  1678. Side **side2p;
  1679. {
  1680.     *side2p = NULL;
  1681.     return TRUE;
  1682. }
  1683.  
  1684. /* Given a pixel in a map, describe what's there. */
  1685.  
  1686. static void
  1687. move_look(side, map, sx, sy)
  1688. Side *side;
  1689. Map *map;
  1690. int sx, sy;
  1691. {
  1692.     int nx, ny;
  1693.     Unit *unit;
  1694.  
  1695.     if (x_nearest_cell(side, map, sx, sy, &nx, &ny)) {
  1696.     if (inside_area(nx, ny)) {
  1697.         x_nearest_unit(side, map, sx, sy, &unit);
  1698.         if (unit != NULL
  1699.         && (side_controls_unit(side, unit) || map->seeall)) {
  1700.         set_current_unit(side, map, unit);
  1701.         } else {
  1702.         set_current_xy(side, map, nx, ny);
  1703.         }
  1704.     } else {
  1705.         beep(side);
  1706.     }
  1707.     }
  1708. }
  1709.  
  1710. /* Set the "current unit" of a map - the one being displayed, moved, etc. */
  1711.  
  1712. void
  1713. set_current_unit(side, map, unit)
  1714. Side *side;
  1715. Map *map;
  1716. Unit *unit;
  1717. {
  1718.     if (unit == map->curunit)
  1719.       return;
  1720.     clear_current(side, map);
  1721.     if (unit == NULL || (in_play(unit) && side_controls_unit(side, unit))) {
  1722.     map->curunit = unit;
  1723.     }
  1724.     /* Always shift the current position to where the unit is,
  1725.        whether or not it's one of ours. */
  1726.     if (unit != NULL) {
  1727.     map->curx = unit->x;  map->cury = unit->y;
  1728.     }
  1729.     draw_current(side, map);
  1730.     draw_map_info(side, map);
  1731.     put_on_screen(side, map, map->curx, map->cury);
  1732. }
  1733.  
  1734. /* Set the "current position" on the map. */
  1735.  
  1736. void
  1737. set_current_xy(side, map, x, y)
  1738. Side *side;
  1739. Map *map;
  1740. int x, y;
  1741. {
  1742.     if (x == map->curx && y == map->cury)
  1743.       return;
  1744.     clear_current(side, map);
  1745.     if (in_area(x, y)) {
  1746.     map->curx = x;  map->cury = y;
  1747.     }
  1748.     draw_current(side, map);
  1749.     draw_map_info(side, map);
  1750.     put_on_screen(side, map, map->curx, map->cury);
  1751. }
  1752.  
  1753. void
  1754. clear_current(side, map)
  1755. Side *side;
  1756. Map *map;
  1757. {
  1758.     int lastx = -1, lasty = -1;
  1759.     Unit *lastunit = NULL;
  1760.  
  1761.     if (map->curunit) {
  1762.     lastunit = map->curunit;
  1763.     } else if (in_area(map->curx, map->cury)) {
  1764.     lastx = map->curx;  lasty = map->cury;
  1765.     }
  1766.     map->curunit = NULL;
  1767.     map->curx = map->cury = -1;
  1768.     erase_current(side, map, lastx, lasty, lastunit);
  1769. }
  1770.  
  1771. /* Save the "cur" slots so we can move around without losing their
  1772.    values. */
  1773.  
  1774. void
  1775. save_cur(side, map)
  1776. Side *side;
  1777. Map *map;
  1778. {
  1779.     map->savedcurx = map->curx;  map->savedcury = map->cury;
  1780.     map->savedcurunit = map->curunit;
  1781. }
  1782.  
  1783. /* Restore the saved "cur" slots. */
  1784.  
  1785. void
  1786. restore_cur(side, map)
  1787. Side *side;
  1788. Map *map;
  1789. {
  1790.     map->curx = map->savedcurx;  map->cury = map->savedcury;
  1791.     map->curunit = map->savedcurunit;
  1792. }
  1793.  
  1794. /* If a given tool (mode) has a special cursor, use it. */
  1795.  
  1796. void
  1797. set_tool_cursor(side, map)
  1798. Side *side;
  1799. Map *map;
  1800. {
  1801.     int tool = map->curtool;
  1802.  
  1803.     if (side->ui->toolcursors[tool] == None) {
  1804.     fprintf(stderr, "No cursor!\n");
  1805.     abort();
  1806.     }
  1807.     XDefineCursor(side->ui->dpy, XtWindow(map->porthole),
  1808.           side->ui->toolcursors[tool]);
  1809. }
  1810.  
  1811. void
  1812. zoom_in_out(side, map, which)
  1813. Side *side;
  1814. Map *map;
  1815. int which;
  1816. {
  1817.     int newpower = map->vp->power + (which == ZOOM_OUT ? -1 : 1);
  1818.  
  1819.     if (newpower < 0)
  1820.       newpower = 0;
  1821.     if (newpower > NUMPOWERS - 1)
  1822.       newpower = NUMPOWERS - 1;
  1823.     if (newpower != map->vp->power) {
  1824.     set_map_power(side, map, newpower);
  1825.     x_center_on_focus(side, map);
  1826.     draw_map(side, map);
  1827.     update_controls(side, map);
  1828.     }
  1829. }
  1830.  
  1831. static void
  1832. mode_callback(w, client_data, call_data)
  1833. Widget w;
  1834. XtPointer client_data;
  1835. XtPointer call_data;
  1836. {
  1837.     int which = (int) client_data;
  1838.     Side *side;
  1839.     Map *map;
  1840.  
  1841.     if (!find_side_and_map_via_control(w, &side, &map))
  1842.       return;
  1843.  
  1844.     map->curtool = which;
  1845.     update_controls(side, map);
  1846.     set_tool_cursor(side, map);
  1847. }
  1848.  
  1849. static void
  1850. command_callback(w, client_data, call_data)
  1851. Widget w;
  1852. XtPointer client_data;
  1853. XtPointer call_data;
  1854. {
  1855.     int which = (int) client_data;
  1856.     Side *side;
  1857.     Map *map;
  1858.  
  1859.     if (!find_side_and_map_via_control(w, &side, &map))
  1860.       return;
  1861.  
  1862.     if (map->curunit == NULL) {
  1863.     beep(side);
  1864.     return;
  1865.     }
  1866.  
  1867.     switch (which) {
  1868.       case UNIT_MOVE:
  1869.     do_move_to(side, map);
  1870.     break;
  1871.       case UNIT_SHOOT:
  1872.     do_fire(side, map);
  1873.     break;
  1874.       case UNIT_BUILD:
  1875.     do_build(side, map);
  1876.     break;
  1877.       default:
  1878.     abort();
  1879.     break;
  1880.     }
  1881. }
  1882.  
  1883. static void
  1884. view_flag_callback(w, client_data, call_data)
  1885. Widget w;
  1886. XtPointer client_data;
  1887. XtPointer call_data;
  1888. {
  1889.     int which = (int) client_data;
  1890.     int redraw = TRUE;
  1891.     Side *side;
  1892.     Map *map;
  1893.  
  1894.     if (!find_side_and_map_via_control(w, &side, &map))
  1895.       return;
  1896.  
  1897.     switch (which) {
  1898.       case SHOW_TERRAIN:
  1899.     map->drawterrain = !map->drawterrain;
  1900.     break;
  1901.       case SHOW_GRID:
  1902.     map->drawgrid = !map->drawgrid;
  1903.     break;
  1904.       case SHOW_UNITS:
  1905.     map->drawunits = !map->drawunits;
  1906.     break;
  1907.       case SHOW_NAMES:
  1908.     map->drawnames = !map->drawnames;
  1909.     break;
  1910.       case SHOW_PEOPLE:
  1911.     map->drawpeople = !map->drawpeople;
  1912.     break;
  1913.       case SHOW_ALL:
  1914.     if (!g_see_all())
  1915.       map->seeall = !map->seeall;
  1916.     else
  1917.       beep(side);
  1918.     break;
  1919.       case SHOW_ELEV:
  1920.     map->drawelevations = !map->drawelevations;
  1921.     break;
  1922.       case SHOW_MORE:
  1923.     if (!map->fullpanel)
  1924.       popup_ctrlpanel(side, map);
  1925.     else
  1926.       popdown_ctrlpanel(side, map);
  1927.     redraw = FALSE;
  1928.     break;
  1929.       default:
  1930.     break;
  1931.     }
  1932.     update_controls(side, map);
  1933.     /* Redraw the map to reflect the effect of the toggle. */
  1934.     if (redraw)
  1935.       draw_map(side, map);
  1936. }
  1937.  
  1938. static void
  1939. zoom_callback(w, client_data, call_data)
  1940. Widget w;
  1941. XtPointer client_data;
  1942. XtPointer call_data;
  1943. {
  1944.     int which = (int) client_data;
  1945.     Side *side;
  1946.     Map *map;
  1947.  
  1948.     if (!find_side_and_map_via_control(w, &side, &map))
  1949.       return;
  1950.  
  1951.     zoom_in_out(side, map, which);
  1952. }
  1953.  
  1954. static void
  1955. unit_type_list_callback(w, client_data, call_data)
  1956. Widget w;
  1957. XtPointer client_data;
  1958. XtPointer call_data;
  1959. {
  1960.     int which = (int) client_data;
  1961.     Side *side;
  1962.     Map *map;
  1963.     void (*fn) PROTO ((Side *sidex, Map *mapx, int cancelledx));
  1964.     
  1965.     if (!find_side_and_map_via_listform(w, &side, &map))
  1966.       return;
  1967.  
  1968.     /* Always ignore clicks on the List label. */
  1969.     if (which == 0)
  1970.       return;
  1971.     /* Call the modal handler if defined. */
  1972.     if (map->modalhandler) {
  1973.     /* Suppress any possible apparent keyboard input, and
  1974.        supply a type directly instead. */
  1975.         map->inpch = '\0';
  1976.     map->inptype = which - 1;
  1977.     fn = map->modalhandler;
  1978.     /* Remove the handler - will restore itself if needed. */
  1979.     map->modalhandler = NULL;
  1980.     (*fn)(side, map, FALSE);
  1981. #ifdef DESIGNERS
  1982.     } else if (side->designer && side->ui->design_shell) {
  1983.     side->ui->curutype = which - 1;
  1984.     update_curutype(side);
  1985.     notify(side, "will now be creating %s %s units",
  1986.            side_adjective(side_n(side->ui->curusidenumber)),
  1987.            u_type_name(side->ui->curutype));
  1988. #endif /* DESIGNERS */
  1989.     }
  1990. }
  1991.  
  1992. static void
  1993. panner_callback(w, client_data, call_data)
  1994. Widget w;
  1995. XtPointer client_data;
  1996. XtPointer call_data;
  1997. {
  1998.     Side *side;
  1999.     Map *map;
  2000.     XawPannerReport *prp = (XawPannerReport *) call_data;
  2001.     
  2002.     if (find_side_and_map_via_rightform(w, &side, &map)) {
  2003.     scroll_map_absolute(side, map, prp->slider_x + hexagon_adjust(map->vp), prp->slider_y);
  2004.     } else
  2005.       run_warning("Did not find side and map!\n");
  2006. }
  2007.  
  2008. static void
  2009. porthole_callback(w, client_data, call_data)
  2010. Widget w;
  2011. XtPointer client_data;
  2012. XtPointer call_data;
  2013. {
  2014.     Side *side;
  2015.     Map *map;
  2016.     XawPannerReport *prp = (XawPannerReport *) call_data;
  2017.     
  2018.     if (!find_side_and_map_via_mapform(w, &side, &map)) {
  2019.     run_warning("Did not find side and map!\n");
  2020.     return;
  2021.     }
  2022.  
  2023.     map->pxw = prp->slider_width;  map->pxh = prp->slider_height;
  2024.     set_view_size(map->vp, prp->slider_width, prp->slider_height);
  2025.     if (map->panner) {
  2026.     XtVaSetValues(map->panner,
  2027.               XtNsliderWidth, map->pxw,
  2028.               XtNsliderHeight, map->pxh,
  2029.               NULL);
  2030.     }
  2031. }
  2032.  
  2033. /* Put the map at an absolute position. */
  2034.  
  2035. void
  2036. scroll_map_absolute(side, map, sx, sy)
  2037. Side *side;
  2038. Map *map;
  2039. int sx, sy;
  2040. {
  2041.     int oldsx = map->vp->sx, oldsy = map->vp->sy;
  2042.  
  2043.     set_view_position(map->vp, sx, sy);
  2044.  
  2045.     /* Redraw if the scroll position actually changed. */
  2046.     if (oldsx != map->vp->sx || oldsy != map->vp->sy)
  2047.       draw_map(side, map);
  2048. }
  2049.  
  2050. #if 0
  2051. void
  2052. scroll_map_relative(side, map, sx, sy)
  2053. Side *side;
  2054. Map *map;
  2055. int sx, sy;
  2056. {
  2057.     int oldsx = map->vp->sx, oldsy = map->vp->sy;
  2058.  
  2059.     /* (genericize into a ui.c routine?) */
  2060.     /* if we're doing horiz scroll */
  2061.     if (sx != 0) {
  2062.     int lx = hexagon_adjust(map->vp);
  2063.     int hx = map->vp->totsw - lx;
  2064.  
  2065.     /* if the map is bigger than the view */
  2066.     if (map->vp->pxw < hx) {
  2067.         map->vp->sx += (sx * (map->vp->pxw - (2 * map->vp->hw)));
  2068.         if (map->vp->sx < lx) {
  2069.         map->vp->sx = (area.xwrap ? hx - (lx - map->vp->sx) : lx);
  2070.         } else if (map->vp->sx > hx) {
  2071.         map->vp->sx = (area.xwrap ? lx + (map->vp->sx - hx) : hx);
  2072.         }
  2073.     }
  2074.     }
  2075.     /* if we're doing vert scroll and there's even reason to */
  2076.     if (sy != 0 && map->vp->pxh < map->vp->totsh) {
  2077.         map->vp->sy += (sy * (map->vp->pxh - (2 * map->vp->hh)));
  2078.         if (map->vp->sy < 0) {
  2079.         map->vp->sy = 0;
  2080.         } else if (map->vp->sy > map->vp->totsh - map->vp->pxh) {
  2081.         map->vp->sy = map->vp->totsh - map->vp->pxh;
  2082.         }
  2083.     }
  2084.     /* Redraw if the scroll position actually changed. */
  2085.     if (oldsx != map->vp->sx || oldsy != map->vp->sy)
  2086.       draw_map(side, map);
  2087. }
  2088. #endif
  2089.  
  2090. static void
  2091. handle_map_click(side, map, sx, sy)
  2092. Side *side;
  2093. Map *map;
  2094. int sx, sy;
  2095. {
  2096.     int x, y;
  2097.     int ax, ay, bx, by, dir;
  2098.     Unit *unit2;
  2099.  
  2100.     if (map == NULL) {
  2101.     beep(side);
  2102.     return;
  2103.     }
  2104.     if (!x_nearest_cell(side, map, sx, sy, &ax, &ay)) {
  2105.     beep(side);
  2106.     return;
  2107.     }
  2108.     /* Assume that last place clicked is a reasonable focus. */
  2109.     if (inside_area(ax, ay)) {
  2110.     set_view_focus(map->vp, ax, ay);
  2111.     }
  2112. #ifdef DESIGNERS
  2113.     if (side->designer && side->ui->curdesigntool != looktool) {
  2114.     handle_designer_map_click(side, map, sx, sy);
  2115.     return;
  2116.     }
  2117. #endif /* DESIGNERS */
  2118.     switch (map->curtool) {
  2119.       case looktool:
  2120.     move_look(side, map, sx, sy);
  2121.     break;
  2122.       case movetool:
  2123.       case unitmovetool:
  2124.     if (map->curunit && side_controls_unit(side, map->curunit)) {
  2125.         move_the_selected_unit(side, map, map->curunit, sx, sy);
  2126.     } else {
  2127.         move_look(side, map, sx, sy);
  2128.     }
  2129.     break;
  2130.       case unitshoottool:
  2131.     if (map->curunit && side_controls_unit(side, map->curunit)) {
  2132.         if (inside_area(ax, ay)) {
  2133.         if ((unit2 = unit_at(ax, ay)) != NULL) {
  2134.             if (map->curunit != unit2) {
  2135.             prep_fire_at_action(map->curunit, map->curunit,
  2136.                         unit2, -1);
  2137.             } else {
  2138.             /* don't attack ourselves */
  2139.             }
  2140.         } else {
  2141.             /* We're just shooting for the hell of it. */
  2142.             prep_fire_into_action(map->curunit, map->curunit,
  2143.                       ax, ay, 0, 0);
  2144.         }
  2145.         } else {
  2146.         beep(side);
  2147.         }
  2148.     } else {
  2149.     }
  2150.     break;
  2151.       case unitbuildtool:
  2152.     /* (what should this do?) */
  2153.     beep(side);
  2154.     break;
  2155.      default:
  2156.     /* error eventually */
  2157.     break;
  2158.     }
  2159. }
  2160.  
  2161. /* (should use advance_into_cell here) */
  2162.  
  2163. void
  2164. move_the_selected_unit(side, map, unit, sx, sy)
  2165. Side *side;
  2166. Map *map;
  2167. Unit *unit;
  2168. int sx, sy;
  2169. {
  2170.     int x, y;
  2171.     Unit *other = NULL;
  2172.  
  2173.     x_nearest_cell(side, map, sx, sy, &x, &y);
  2174. #ifdef DESIGNERS
  2175.     /* Designers use this function to push units around, bound only by the
  2176.        limits on occupancy. */
  2177.     if (side->designer) {
  2178.     x_nearest_unit(side, map, sx, sy, &other);
  2179.     /* Teleport the unit to its new location. */
  2180.     if (other != NULL && can_occupy(unit, other)) {
  2181.         leave_cell(unit);
  2182.         enter_transport(unit, other);
  2183.     } else if (can_occupy_cell(unit, x, y)) {
  2184.         leave_cell(unit);
  2185.         enter_cell(unit, x, y);
  2186.     } else {
  2187.         beep(side);
  2188.     }
  2189.     return;
  2190.     }
  2191. #endif
  2192.     if (x != unit->x || y != unit->y) {
  2193.     /* we're outa here... leaving the cell that is */
  2194.  
  2195.     if (unit->act && unit->plan) { /* (should be more sophisticated?) */
  2196.  
  2197.         /* if it's far away, set up the task and boogie */
  2198.         if (distance(unit->x, unit->y, x, y) > 1) {
  2199.         DGprintf("Ordering %s to move to %d,%d\n",
  2200.              unit_desig(unit), x, y);
  2201.         set_moveto_task(unit, x, y);
  2202.         return;
  2203.         }
  2204.  
  2205.         /* If no one's home, try to move into it directly. */
  2206.         if (unit_at(x, y) == NULL) {
  2207.         if (can_occupy_cell(unit, x, y)
  2208.             && valid(check_move_action(unit, unit, x, y, 0))) {
  2209.             prep_move_action(unit, unit, x, y, 0);
  2210.         } else {
  2211.             beep(side);
  2212.         }
  2213.         return;
  2214.         }
  2215.  
  2216.         /* There are units at our desired destination. */
  2217.         x_nearest_unit(side, map, sx, sy, &other);
  2218.         if (other == NULL) {
  2219.         if (can_occupy_cell(unit, x, y)) {
  2220.             prep_move_action(unit, unit, x, y, 0);
  2221.         } else {
  2222.             beep(side);
  2223.         }
  2224.         } else if (other->side == unit->side) {
  2225.         /* One of ours, maybe get on it. */
  2226.         if (can_occupy(unit, other)) {
  2227.             prep_enter_action(unit, unit, other);
  2228.         } else if (can_occupy(other, unit)) {
  2229.             /* Have other unit do an enter action, then move. */
  2230.             /* (not quite right, move should happen after other unit
  2231.                is actually inside, in case it fills dest) */
  2232.             prep_enter_action(other, other, unit);
  2233.             order_moveto(unit, x, y);
  2234.         } else if (can_occupy_cell(unit, x, y)) {
  2235.             prep_move_action(unit, unit, x, y, 0);
  2236.         } else {
  2237.             beep(side);
  2238.         }
  2239.         } else {
  2240.         /* Somebody else's unit, try to victimize it. */
  2241.         if (valid(check_capture_action(unit, unit, other))) {
  2242.             prep_capture_action(unit, unit, other);
  2243.         } else if (valid(check_attack_action(unit, unit, other, 100))) {
  2244.             prep_attack_action(unit, unit, other, 100);
  2245.         } else {
  2246.             beep(side);
  2247.         }
  2248.         }
  2249.     }
  2250.     } else {
  2251.     /* moving around within the current cell */
  2252.  
  2253.     x_nearest_unit(side, map, sx, sy, &other);
  2254.     if (other != NULL && other != unit) {
  2255.         /* ok we're trying to hop onto another transport */
  2256.         if (can_occupy(unit, other)) {
  2257.         prep_enter_action(unit, unit, other);
  2258.         } else {
  2259.         /* maybe we should restack? */
  2260.         beep(side);
  2261.         }
  2262.     }
  2263.     }
  2264. }
  2265.  
  2266. void
  2267. destroy_map(side, map)
  2268. Side *side;
  2269. Map *map;
  2270. {
  2271.     /* find map and remove it from the list of maps */
  2272.     /* also put some other map in front if this one was the front one */
  2273. }
  2274.  
  2275. static void
  2276. panner_resize_handler (w, client_data, event, cont)
  2277. Widget w; 
  2278. XtPointer client_data; 
  2279. XEvent *event; 
  2280. Boolean *cont;
  2281. {
  2282.     Side *side;
  2283.     Map *map;
  2284.  
  2285.     if (event->type == ConfigureNotify) {
  2286.     for_all_sides(side) {
  2287.         if (active_display(side)) {
  2288.         if (XtDisplay(w) == side->ui->dpy) {
  2289.             for_all_maps(side, map) {
  2290.             if (w == map->panner) {
  2291.                 goto found;
  2292.             }
  2293.             }
  2294.         }
  2295.         }
  2296.     }
  2297.     return;
  2298.  
  2299.       found:
  2300.     draw_view_in_panner(side, map);
  2301.     } else {
  2302.     return;
  2303.     }
  2304. }
  2305.  
  2306. /* draw the world view in the background of the panner */
  2307.  
  2308. void
  2309. draw_view_in_panner(side, map)
  2310. Side *side;
  2311. Map *map;
  2312. {
  2313.     static Pixmap pix = None;
  2314.     int x, y, l, t, mx, my, depth;
  2315.     Dimension w = 0, h = 0, b = 0;
  2316.     Display *dpy = side->ui->dpy;
  2317.     Pixel pixel, bg=0, unknown;
  2318.     XImage *img;
  2319.     char *dp, *data;
  2320.     GC gc;
  2321.     Widget dummy;
  2322.  
  2323.     if (map->panner == NULL)
  2324.       return;
  2325.  
  2326.     unknown = XBlackPixel(dpy, side->ui->screen);
  2327.     depth = DefaultDepth(dpy, side->ui->screen);
  2328.     if (depth % 8)
  2329.       return;
  2330.  
  2331.     XtVaGetValues(map->panner,
  2332.           XtNwidth, &w,
  2333.           XtNheight, &h,
  2334.           XtNinternalSpace, &b,
  2335.           XtNbackground, &bg,
  2336.           NULL);
  2337.  
  2338.     /* b is the size of the internal border  */
  2339.     if (b <= 0 || h <= 2 * b || w <= 2 * b)
  2340.       return;
  2341.  
  2342.     data = (char *) malloc(w * h * depth / 8 * sizeof(char));
  2343.  
  2344.     dp = data;
  2345.     for (y = 0; y < h; y++) {
  2346.     if (y < b || y >= h - b) {
  2347.         my = -1;
  2348.     } else {
  2349.         my = ((h - 1 - b - y) * area.height) / (h - 2 * b);
  2350.     }
  2351.     for (x = 0; x < w; x++) {
  2352.         if (x < b || x >= w - b) {
  2353.         mx = -1;
  2354.         } else {
  2355.             /* get the right rounding for the division by 2 */
  2356.         mx = ((2 * (x - b) * area.width) / (w - 2 * b) - my) / 2;
  2357.         if (area.xwrap) {
  2358.             mx = wrapx(mx);
  2359.         } else {
  2360.             mx += area.height / 4;
  2361.         }
  2362.         }
  2363.         if (mx >= 0 && my >= 0 && in_area(mx, my)) {
  2364.         t = seen_terrain_at(mx, my, side);
  2365.         if (t == NONTTYPE) {
  2366.             pixel = unknown;
  2367.         } else {
  2368.             pixel = side->ui->cellcolor[t];
  2369.         }
  2370.         } else {
  2371.         pixel = bg;
  2372.         }
  2373.         for (l = depth - 8; l >= 0; l -= 8) {
  2374.         *dp = (pixel >> l) & 0xff;
  2375.         dp++;
  2376.         }
  2377.     }
  2378.     }
  2379.  
  2380.     img = XCreateImage(dpy, DefaultVisual(dpy, side->ui->screen), depth,
  2381.                ZPixmap, 0, data, w, h, 8, w * depth / 8);
  2382.     if (img == NULL) {
  2383.     free(data);
  2384.     return;
  2385.     }
  2386.     img->byte_order = MSBFirst;
  2387.     img->bitmap_bit_order = MSBFirst;
  2388.  
  2389.     /* clear the panner background before freeing the pixmap */    
  2390. #ifdef NO_PANNER_BGPIXMAP
  2391.     XSetWindowBackgroundPixmap(dpy, XtWindow(map->panner), None);
  2392. #else
  2393.     XtVaSetValues(map->panner, XtNbackgroundPixmap, None, NULL);
  2394. #endif
  2395.  
  2396.     if (pix != None)
  2397.       XFreePixmap(dpy, pix);
  2398.     pix = XCreatePixmap(dpy, side->ui->rootwin, w, h, depth);
  2399.     if (!pix || pix == None) {
  2400.     XDestroyImage(img);
  2401.     /* XDestroyImage also frees data */
  2402.     return;
  2403.     }
  2404.  
  2405.     gc = XCreateGC(dpy, pix, 0, NULL);
  2406.     XPutImage(dpy, pix, gc, img, 0, 0, 0, 0, w, h);
  2407.     XFreeGC(dpy, gc);
  2408.     XDestroyImage(img);
  2409.     /* XDestroyImage also frees data */
  2410.  
  2411.     if (pix!=None) {
  2412. #ifdef NO_PANNER_BGPIXMAP
  2413.     XSetWindowBackgroundPixmap(dpy, XtWindow(map->panner), pix);
  2414. #else
  2415.     XtVaSetValues(map->panner, XtNbackgroundPixmap, pix, NULL);
  2416. #endif
  2417.     }
  2418.  
  2419. #ifdef NO_PANNER_BGPIXMAP
  2420.     /* at this point we must force a redraw of the panner. If you 
  2421.        know a better way, mail me at campo@sunthpi3.difi.unipi.it */
  2422.     /* we should maybe use XPutBackEvent */
  2423.     dummy =
  2424.       XtVaCreateManagedWidget("dummy", labelWidgetClass, map->pannerbox,
  2425.                   XtNfromVert, map->listview,
  2426.                   XtNwidth, w,
  2427.                   XtNheight, h,
  2428.                   XtNlabel, "",
  2429.                   NULL);
  2430.     XFlush(dpy);
  2431.     XtDestroyWidget(dummy);
  2432. #endif /* NO_PANNER_BGPIXMAP */
  2433. }
  2434.  
  2435. /* Popup control panel for maps, written by Massimo Campostrini. */
  2436.  
  2437. static void
  2438. popup_ctrlpanel(side, map)
  2439. Side *side;
  2440. Map *map;
  2441. {
  2442.     if (!map->ctrlpanel_shell)
  2443.       create_ctrlpanel(side, map);
  2444.     map->fullpanel = TRUE;
  2445.     XtPopup(map->ctrlpanel_shell, XtGrabNone);
  2446. }
  2447.  
  2448. int
  2449. find_side_and_map_via_ctrlpanel_form(w, sidep, mapp)
  2450. Widget w;
  2451. Side **sidep;
  2452. Map **mapp;
  2453. {
  2454.     Side *side;
  2455.     Map *map;
  2456.  
  2457.     for_all_sides(side) {
  2458.     if (active_display(side)) {
  2459.         if (XtDisplay(w) == side->ui->dpy) {
  2460.         for_all_maps(side, map) {
  2461.             if (map->ctrlpanel_form == w) {
  2462.             *sidep = side;
  2463.             *mapp = map;
  2464.             return TRUE;
  2465.             }
  2466.         }
  2467.         }
  2468.     }
  2469.     }
  2470.     return FALSE;
  2471. }
  2472.  
  2473. static void
  2474. create_ctrlpanel(side, map)
  2475. Side *side;
  2476. Map *map;
  2477. {
  2478.     Widget *buttons, label;
  2479.  
  2480.     map->ctrlpanel_shell =
  2481.       XtVaCreatePopupShell("controlPanel", transientShellWidgetClass, side->ui->shell,
  2482.                NULL);
  2483.  
  2484.     map->ctrlpanel_form =
  2485.       XtVaCreateManagedWidget("form", boxWidgetClass, map->ctrlpanel_shell,
  2486.                   NULL);
  2487.  
  2488.     buttons = (Widget *) xmalloc(numcontrols * sizeof (Widget));
  2489.     map->ctrlpanel_buttons = buttons;
  2490.  
  2491.     label =
  2492.       XtVaCreateManagedWidget("---what---", labelWidgetClass, map->ctrlpanel_form,
  2493.                   XtNborderWidth, 0,
  2494.                   NULL);
  2495.  
  2496.     map->ctrlpanel_buttons[SHOW_TERRAIN] =
  2497.       XtVaCreateManagedWidget("Terrain", toggleWidgetClass, map->ctrlpanel_form,
  2498.                   NULL);
  2499.     map->ctrlpanel_buttons[SHOW_GRID] =
  2500.       XtVaCreateManagedWidget("Grid", toggleWidgetClass, map->ctrlpanel_form,
  2501.                   NULL);
  2502.     map->ctrlpanel_buttons[SHOW_UNITS] =
  2503.       XtVaCreateManagedWidget("Units", toggleWidgetClass, map->ctrlpanel_form,
  2504.                   NULL);
  2505.     map->ctrlpanel_buttons[SHOW_NAMES] =
  2506.       XtVaCreateManagedWidget("Names", toggleWidgetClass, map->ctrlpanel_form,
  2507.                   NULL);
  2508.     map->ctrlpanel_buttons[SHOW_FEATURE_NAMES] =
  2509.       XtVaCreateManagedWidget("Feature Names", toggleWidgetClass, map->ctrlpanel_form,
  2510.                   NULL);
  2511.     map->ctrlpanel_buttons[SHOW_PEOPLE] =
  2512.       XtVaCreateManagedWidget("People", toggleWidgetClass, map->ctrlpanel_form,
  2513.                   NULL);
  2514.     map->ctrlpanel_buttons[SHOW_ELEV] =
  2515.       XtVaCreateManagedWidget("Elevations", toggleWidgetClass, map->ctrlpanel_form,
  2516.                   NULL);
  2517.     map->ctrlpanel_buttons[SHOW_TEMP] =
  2518.       XtVaCreateManagedWidget("Temperatures", toggleWidgetClass, map->ctrlpanel_form,
  2519.                   NULL);
  2520.     map->ctrlpanel_buttons[SHOW_ALL] =
  2521.       XtVaCreateManagedWidget("See All", toggleWidgetClass, map->ctrlpanel_form,
  2522.                   XtNsensitive, !g_see_all(),
  2523.                   NULL);
  2524.  
  2525.     label =
  2526.       XtVaCreateManagedWidget("--color---", labelWidgetClass, map->ctrlpanel_form,
  2527.                   XtNborderWidth, 0,
  2528.                   NULL);
  2529.  
  2530.     map->ctrlpanel_buttons[COLR_TERR] =
  2531.      XtVaCreateManagedWidget("Terrain", toggleWidgetClass, map->ctrlpanel_form,
  2532.                   XtNsensitive, !side->ui->monochrome,
  2533.                   NULL);
  2534.     map->ctrlpanel_buttons[COLR_UNITS] =
  2535.      XtVaCreateManagedWidget(" Units ", toggleWidgetClass, map->ctrlpanel_form,
  2536.                   XtNsensitive, !side->ui->monochrome,
  2537.                   NULL);
  2538.     map->ctrlpanel_buttons[COLR_EMBL] =
  2539.      XtVaCreateManagedWidget("Emblems", toggleWidgetClass, map->ctrlpanel_form,
  2540.                   XtNsensitive, !side->ui->monochrome,
  2541.                   NULL);
  2542.     label =
  2543.       XtVaCreateManagedWidget("---mono---", labelWidgetClass, map->ctrlpanel_form,
  2544.                   XtNborderWidth, 0,
  2545.                   NULL);
  2546.     map->ctrlpanel_buttons[MONO_REVERSE] =
  2547.       XtVaCreateManagedWidget("Reverse", toggleWidgetClass, map->ctrlpanel_form,
  2548.                   XtNsensitive, side->ui->monochrome,
  2549.                   NULL);
  2550.  
  2551.     label =
  2552.       XtVaCreateManagedWidget("----------", labelWidgetClass, map->ctrlpanel_form,
  2553.                   XtNborderWidth, 0,
  2554.                   NULL);
  2555.     label =
  2556.       XtVaCreateManagedWidget("Cancel", commandWidgetClass, map->ctrlpanel_form,
  2557.                   NULL);
  2558.     XtAddCallback(label, XtNcallback, ctrlpanel_cancel_callback, NULL);
  2559.     label =
  2560.       XtVaCreateManagedWidget("Revert", commandWidgetClass, map->ctrlpanel_form,
  2561.                   NULL);
  2562.     XtAddCallback(label, XtNcallback, ctrlpanel_revert_callback, NULL);
  2563.     label =
  2564.       XtVaCreateManagedWidget("Apply", commandWidgetClass, map->ctrlpanel_form,
  2565.                   NULL);
  2566.     XtAddCallback(label, XtNcallback, ctrlpanel_apply_callback, NULL);
  2567.     label =
  2568.       XtVaCreateManagedWidget("Done", commandWidgetClass, map->ctrlpanel_form,
  2569.                   NULL);
  2570.     XtAddCallback(label, XtNcallback, ctrlpanel_done_callback, NULL);
  2571. }
  2572.  
  2573. static void 
  2574. ctrlpanel_cancel_callback(w, client_data, call_data)
  2575. Widget w;
  2576. XtPointer client_data;
  2577. XtPointer call_data;
  2578. {
  2579.     Side *side;
  2580.     Map *map;
  2581.  
  2582.     if (!find_side_and_map_via_ctrlpanel_form(XtParent(w), &side, &map))
  2583.       return;
  2584.  
  2585.     popdown_ctrlpanel(side, map);
  2586. }
  2587.  
  2588. static void 
  2589. ctrlpanel_revert_callback(w, client_data, call_data)
  2590. Widget w;
  2591. XtPointer client_data;
  2592. XtPointer call_data;
  2593. {
  2594.     Side *side;
  2595.     Map *map;
  2596.  
  2597.     if (!find_side_and_map_via_ctrlpanel_form(XtParent(w), &side, &map))
  2598.       return;
  2599.  
  2600.     update_controls(side, map);
  2601. }
  2602.  
  2603. int get_toggle_state PROTO ((Widget w));
  2604.  
  2605. int
  2606. get_toggle_state(w)
  2607. Widget w;
  2608. {
  2609.     Boolean rslt;
  2610.  
  2611.     XtVaGetValues(w, XtNstate, &rslt, NULL);
  2612.     return rslt; 
  2613. }
  2614.  
  2615. static int set_from_controls PROTO ((Side *side, Map *map));
  2616.  
  2617. static int
  2618. set_from_controls(side, map)
  2619. Side *side;
  2620. Map *map;
  2621. {
  2622.     int newval, anychanged = FALSE;
  2623.  
  2624.     newval = get_toggle_state(map->ctrlpanel_buttons[SHOW_TERRAIN]);
  2625.     if (newval != map->drawterrain)
  2626.       anychanged = TRUE;
  2627.     map->drawterrain = newval;
  2628.     newval = get_toggle_state(map->ctrlpanel_buttons[SHOW_GRID]);
  2629.     if (newval != map->drawgrid)
  2630.       anychanged = TRUE;
  2631.     map->drawgrid = newval;
  2632.     newval = get_toggle_state(map->ctrlpanel_buttons[SHOW_UNITS]);
  2633.     if (newval != map->drawunits)
  2634.       anychanged = TRUE;
  2635.     map->drawunits = newval;
  2636.     newval = get_toggle_state(map->ctrlpanel_buttons[SHOW_NAMES]);
  2637.     if (newval != map->drawnames)
  2638.       anychanged = TRUE;
  2639.     map->drawnames = newval;
  2640.     newval = get_toggle_state(map->ctrlpanel_buttons[SHOW_FEATURE_NAMES]);
  2641.     if (newval != map->drawfeaturenames)
  2642.       anychanged = TRUE;
  2643.     map->drawfeaturenames = newval;
  2644.     newval = get_toggle_state(map->ctrlpanel_buttons[SHOW_PEOPLE]);
  2645.     if (newval != map->drawpeople)
  2646.       anychanged = TRUE;
  2647.     map->drawpeople = newval;
  2648.     newval = get_toggle_state(map->ctrlpanel_buttons[SHOW_ELEV]);
  2649.     if (newval != map->drawelevations)
  2650.       anychanged = TRUE;
  2651.     map->drawelevations = newval;
  2652.     newval = get_toggle_state(map->ctrlpanel_buttons[SHOW_TEMP]);
  2653.     if (newval != map->drawtemp)
  2654.       anychanged = TRUE;
  2655.     map->drawtemp = newval;
  2656.     newval = get_toggle_state(map->ctrlpanel_buttons[SHOW_ALL]);
  2657.     if (newval != map->seeall)
  2658.       anychanged = TRUE;
  2659.     map->seeall = newval;
  2660.     /* (shouldn't be affecting all maps without redrawing all...) */
  2661.     newval = get_toggle_state(map->ctrlpanel_buttons[COLR_TERR]);
  2662.     if (newval != side->ui->dflt_color_terr_images)
  2663.       anychanged = TRUE;
  2664.     side->ui->dflt_color_terr_images = newval;
  2665.     newval = get_toggle_state(map->ctrlpanel_buttons[COLR_UNITS]);
  2666.     if (newval != side->ui->dflt_color_unit_images)
  2667.       anychanged = TRUE;
  2668.     side->ui->dflt_color_unit_images = newval;
  2669.     newval = get_toggle_state(map->ctrlpanel_buttons[COLR_EMBL]);
  2670.     if (newval != side->ui->dflt_color_embl_images)
  2671.       anychanged = TRUE;
  2672.     side->ui->dflt_color_embl_images = newval;
  2673.     return anychanged;
  2674. }
  2675.  
  2676. static void 
  2677. ctrlpanel_apply_callback(w, client_data, call_data)
  2678. Widget w;
  2679. XtPointer client_data;
  2680. XtPointer call_data;
  2681. {
  2682.     Side *side;
  2683.     Map *map;
  2684.     int changed;
  2685.  
  2686.     if (!find_side_and_map_via_ctrlpanel_form(XtParent(w), &side, &map))
  2687.       return;
  2688.  
  2689.     changed = set_from_controls(side, map);
  2690.     if (changed)
  2691.       draw_map(side, map);
  2692. }
  2693.  
  2694. static void 
  2695. ctrlpanel_done_callback(w, client_data, call_data)
  2696. Widget w;
  2697. XtPointer client_data;
  2698. XtPointer call_data;
  2699. {
  2700.     Side *side;
  2701.     Map *map;
  2702.     int changed;
  2703.  
  2704.     if (!find_side_and_map_via_ctrlpanel_form(XtParent(w), &side, &map))
  2705.       return;
  2706.  
  2707.     changed = set_from_controls(side, map);
  2708.     popdown_ctrlpanel(side, map);
  2709.     if (changed)
  2710.       draw_map(side, map);
  2711. }
  2712.  
  2713. static void
  2714. popdown_ctrlpanel(side, map)
  2715. Side *side;
  2716. Map *map;
  2717. {
  2718.     map->fullpanel = FALSE;
  2719.     if (map->ctrlpanel_shell)
  2720.       XtPopdown(map->ctrlpanel_shell);
  2721. }
  2722.  
  2723. void set_control_state PROTO ((Side *side, Map *map, int, int, int));
  2724.  
  2725. void
  2726. update_controls(side, map)
  2727. Side *side;
  2728. Map *map;
  2729. {
  2730.     set_control_state(side, map, LOOK, TRUE, (map->curtool == looktool));
  2731.     set_control_state(side, map, MOVE, TRUE, (map->curtool == movetool));
  2732.     set_control_state(side, map, UNIT_MOVE, (map->curunit != NULL && can_move_at_all(map->curunit)), FALSE);
  2733.     set_control_state(side, map, UNIT_SHOOT, (map->curunit != NULL), FALSE);
  2734.     set_control_state(side, map, UNIT_BUILD, (map->curunit != NULL), FALSE);
  2735.     set_control_state(side, map, SHOW_TERRAIN, TRUE, map->drawterrain);
  2736.     set_control_state(side, map, SHOW_GRID, TRUE, map->drawgrid);
  2737.     set_control_state(side, map, SHOW_UNITS, TRUE, map->drawunits);
  2738.     set_control_state(side, map, SHOW_NAMES, TRUE, map->drawnames);
  2739.     set_control_state(side, map, SHOW_FEATURE_NAMES, TRUE, map->drawfeaturenames);
  2740.     set_control_state(side, map, SHOW_PEOPLE, people_sides_defined(), map->drawpeople);
  2741.     set_control_state(side, map, SHOW_ELEV, elevations_defined(), map->drawelevations);
  2742.     set_control_state(side, map, SHOW_TEMP, temperatures_defined(), map->drawtemp);
  2743.     set_control_state(side, map, SHOW_ALL, !g_see_all() && side->ui->mayseeall, map->seeall);
  2744.     set_control_state(side, map, SHOW_MORE, TRUE, map->fullpanel);
  2745.     set_control_state(side, map, ZOOM_OUT, (map->vp->power > 0), FALSE);
  2746.     set_control_state(side, map, ZOOM_IN,  (map->vp->power < NUMPOWERS - 1), FALSE);
  2747.     set_control_state(side, map, COLR_TERR, !side->ui->monochrome,
  2748.               side->ui->dflt_color_terr_images);
  2749.     set_control_state(side, map, COLR_UNITS, !side->ui->monochrome,
  2750.               side->ui->dflt_color_unit_images);
  2751.     set_control_state(side, map, COLR_EMBL, !side->ui->monochrome,
  2752.               side->ui->dflt_color_embl_images);
  2753. }
  2754.  
  2755. void
  2756. set_control_state(side, map, contype, active, state)
  2757. Side *side;
  2758. Map *map;
  2759. int contype, active, state;
  2760. {
  2761.     nargs = 0;
  2762.     XtSetArg(tmpargs[nargs], XtNsensitive, (Boolean) active);  nargs++;
  2763.     /* Note that the XtNstate property can be set for command widgets,
  2764.        and will simply have no effect. */
  2765.     XtSetArg(tmpargs[nargs], XtNstate, (Boolean) state);  nargs++;
  2766.     if (map->controls[contype])
  2767.       XtSetValues(map->controls[contype], tmpargs, nargs);
  2768.     if (map->fullpanel && map->ctrlpanel_shell && map->ctrlpanel_buttons[contype])
  2769.       XtSetValues(map->ctrlpanel_buttons[contype], tmpargs, nargs);
  2770. }
  2771.  
  2772. void
  2773. set_message_area(map, msg)
  2774. Map *map;
  2775. char *msg;
  2776. {
  2777.     /* Can we? */
  2778.     if (!map->msgarea)
  2779.       return;
  2780.     nargs = 0;
  2781.     XtSetArg(tmpargs[nargs], XtNlabel, msg);    nargs++;
  2782.     XtSetValues(map->msgarea, tmpargs, nargs);
  2783. }
  2784.  
  2785. /* Prompt line drawing. */
  2786.  
  2787. void
  2788. draw_prompt(side, map)
  2789. Side *side;
  2790. Map *map;
  2791. {
  2792.     char tmpbuf[BUFSIZE];
  2793.  
  2794.     if (map == NULL)
  2795.       return;
  2796.     sprintf(tmpbuf, "%s %s", map->prompt, map->answer);
  2797.     nargs = 0;
  2798.     XtSetArg(tmpargs[nargs], XtNlabel, tmpbuf);  nargs++;
  2799.     XtSetValues(map->promptlabel, tmpargs, nargs);
  2800.     /* should change the cursor so that the user knows that an
  2801.        answer is expected. */
  2802. }
  2803.  
  2804. void
  2805. clear_prompt(side, map)
  2806. Side *side;
  2807. Map *map;
  2808. {
  2809.     if (map == NULL)
  2810.       return;
  2811.     map->prompt[0] = '\0';
  2812.     map->answer[0] = '\0';
  2813.     nargs = 0;
  2814.     XtSetArg(tmpargs[nargs], XtNlabel, " ");  nargs++;
  2815.     XtSetValues(map->promptlabel, tmpargs, nargs);
  2816. }
  2817.  
  2818.  
  2819. void
  2820. draw_game_state(side, map)
  2821. Side *side;
  2822. Map *map;
  2823. {
  2824.     char tmpbuf[BUFSIZE];
  2825.     extern char *curseasonname;
  2826.     extern int paused;
  2827.  
  2828.     if (map == NULL || map->gamedate == None)
  2829.       return;
  2830.     sprintf(tmpbuf, "%s", absolute_date_string(g_turn()));
  2831.     if (curseasonname != NULL)
  2832.       tprintf(tmpbuf, " (%s)", curseasonname);
  2833.     if (paused)
  2834.       tprintf(tmpbuf, " PAUSED");
  2835.     XtVaSetValues(map->gamedate, XtNlabel, tmpbuf, NULL);
  2836.     draw_game_clocks(side, map);
  2837. }
  2838.  
  2839. /* This displays the gamewide clock(s) if defined. */
  2840.  
  2841. void
  2842. draw_game_clocks(side, map)
  2843. Side *side;
  2844. Map *map;
  2845. {
  2846.     int elapsed;
  2847.     time_t now;
  2848.     char tmpbuf[BUFSIZE];
  2849.  
  2850.     if (map == NULL || map->gameclock == None)
  2851.       return;
  2852.     nargs = 0;
  2853.     XtSetArg(tmpargs[nargs], XtNlabel, " ");  nargs++;
  2854.     XtSetValues(map->gameclock, tmpargs, nargs);
  2855.     if (g_rt_for_game() > 0) {
  2856.     time(&now);
  2857.     elapsed = idifftime(now, game_start_in_real_time);
  2858.     time_desc(tmpbuf, max(0, g_rt_for_game() - elapsed), g_rt_for_game());
  2859.     nargs = 0;
  2860.     XtSetArg(tmpargs[nargs], XtNlabel, tmpbuf);  nargs++;
  2861.     XtSetValues(map->gameclock, tmpargs, nargs);
  2862.     }
  2863.     /* (should not overwrite prev, if both defined) */
  2864.     if (g_rt_per_turn() > 0) {
  2865.     time(&now);
  2866.     elapsed = idifftime(now, turn_play_start_in_real_time);
  2867.     time_desc(tmpbuf, max(0, g_rt_per_turn() - elapsed), g_rt_per_turn());
  2868.     nargs = 0;
  2869.     XtSetArg(tmpargs[nargs], XtNlabel, tmpbuf);  nargs++;
  2870.     XtSetValues(map->gameclock, tmpargs, nargs);
  2871.     }
  2872. }
  2873.  
  2874. /* Alter the numbers for a single type of unit.  Should be called right
  2875.    after any changes.  Formatted to look nice, but kind of messy to set
  2876.    up correctly; display should not jump back and forth as the numbers
  2877.    change in size. */
  2878.  
  2879. void
  2880. update_unit_type_list(side, map, u)
  2881. Side *side;
  2882. Map *map;
  2883. int u;
  2884. {
  2885.     int num, changed = FALSE;
  2886.  
  2887.     if (!between(0, u, numutypes))
  2888.       return;
  2889.     sprintf(spbuf, "");
  2890.     /* Our unit total (right-justified) */
  2891.     num = num_units_in_play(side, u);
  2892.     if (num != map->last_num_in_play[u]) {
  2893.     map->last_num_in_play[u] = num;
  2894.     changed = TRUE;
  2895.     }
  2896.     if (num > 0)    {
  2897.     sprintf(tmpbuf, "%4d", num);
  2898.     } else {
  2899.     sprintf(tmpbuf, "    ");
  2900.     }
  2901.     strcat(spbuf, tmpbuf);
  2902.     /* Our units under construction (left-justified) */
  2903.     num = num_units_incomplete(side, u);
  2904.     if (num != map->last_num_incomplete[u]) {
  2905.     map->last_num_incomplete[u] = num;
  2906.     changed = TRUE;
  2907.     }
  2908.     if (num > 0) {
  2909.     sprintf(tmpbuf, "(%d)", num);
  2910.     } else {
  2911.     sprintf(tmpbuf, "    ");
  2912.     }
  2913.     strcat(spbuf, tmpbuf);
  2914.     /* (should do other columns too) */
  2915.     if (changed)
  2916.       XtVaSetValues(map->list_buttons[u + 1], XtNlabel, spbuf, NULL);
  2917. }
  2918.  
  2919. void
  2920. place_legends(side)
  2921. Side *side;
  2922. {
  2923.     int nf = side->ui->numfeatures;
  2924.  
  2925.     if (!features_defined() || nf <= 0)
  2926.       return;
  2927.     if (side->ui->legends == NULL)
  2928.       side->ui->legends = (Legend *) xmalloc((nf + 1) * sizeof(Legend));
  2929.     place_feature_legends(side->ui->legends, nf, side, 0, 1);
  2930. }
  2931.